Cortex XQL Command Reference

Tree view

This book on one page — search it with your browser’s find (Ctrl+F / ⌘F), or jump from the tree.

Cortex XQL Command Reference

Cortex XQL (Extended Query Language) is a powerful query language used in the Cortex platform for threat hunting, investigation, and analytics across your security data. This reference provides comprehensive documentation for all XQL functions and pipeline stages.

XQL queries are composed of stages connected in a pipeline, with functions used within those stages to transform, filter, and analyze data. This reference is organized into two main sections:

  • Functions – Built-in functions, indexes, and detailed reference pages.
  • Stages – Pipeline stages, indexes, and detailed reference pages.

Functions

Function Description
acos Calculate the inverse cosine (arccosine) of a numerical expression
add  
approx_count  
approx_quantiles  
approx_top  
asin Calculate the inverse sine (arcsine) of a numerical expression
array_all  
array_any  
array_length  
arrayconcat  
arraycreate  
arraydistinct  
arrayfilter  
arrayindex  
arrayindexof  
arraymap  
arraymerge  
arrayrange  
arraystring  
avg  
avg  
bitwise_and Perform a bitwise AND operation between two integer values
bitwise_or Perform a bitwise OR operation between two integer values
bitwise_sleft Perform a bitwise left shift operation on an integer value
bitwise_sright Perform a bitwise right shift operation on an integer value
bitwise_xor Perform a bitwise exclusive OR (XOR) operation between two integer values
cbrt Calculate the cube root of a numeric value
ceil Round a number up to the nearest integer
coalesce  
concat  
convert_from_base_64  
convert_to_base_64  
cos Calculate the cosine of a numeric value specified in radians
cosine_distance Calculate the cosine distance between two numeric vectors
cot Calculate the cotangent of a numeric value specified in radians
count  
count  
count_distinct  
csc Calculate the cosecant of a numeric value specified in radians
current_time  
date_floor  
divide  
earliest  
euclidean_distance Calculate the Euclidean distance between two numeric vectors
exp Calculate the value of e raised to the power of a numeric value
extract_time  
extract_url_host  
extract_url_pub_suffix  
extract_url_registered_domain  
first  
first_value  
floor  
format_string  
format_timestamp  
greatest Return the largest value from a list of expressions
if  
incidr  
incidr6  
incidrlist  
int_to_ip  
ip_to_int  
is_ipv4  
is_ipv6  
is_known_private_ipv4  
is_known_private_ipv6  
json_extract  
json_extract_array  
json_extract_scalar  
json_extract_scalar_array  
json_path_extract  
json_functions_reference A comprehensive guide to the four JSON extraction functions
lag  
last  
last_value  
latest  
least Return the smallest value from a list of expressions
len  
list (comp) Collect all values of a field and return them as an array within the comp stage
ln Calculate the natural logarithm (base e) of a numeric value
log Calculate the logarithm of a numeric value with a specified base
log10 Calculate the base-10 logarithm of a numeric value
lowercase  
ltrim  
max (comp) Return the maximum value of a field within the comp stage
max (windowcomp) Compute the maximum value of a field over a window of rows within the windowcomp stage
md5  
median (comp) Return the median value of a numeric field within the comp stage
median (windowcomp) Compute the median value of a numeric field over a window of rows within the windowcomp stage
min (comp) Return the minimum value of a field within the comp stage
min (windowcomp) Compute the minimum value of a field over a window of rows within the windowcomp stage
mod Calculate the remainder (modulus) of the division of two numeric values
multiply  
object_create  
object_merge  
parse_epoch  
parse_timestamp  
pow  
power Raise a number to the power of another number (alias for pow)
rand Generate a pseudo-random floating-point number between 0 and 1
range_bucket Determine which bucket a numeric value falls into given an array of boundaries
rank (windowcomp) Assign a rank to each row within a partition in the windowcomp stage
regexcapture  
regextract Extract a substring from a field value using a regular expression pattern
replace  
replex  
round  
row_number (windowcomp) Assign a unique sequential integer to each row within a partition in the windowcomp stage
rtrim  
safe_add Perform addition with overflow protection, returning null on overflow
safe_divide Perform division with error protection, returning null on division by zero
safe_multiply Perform multiplication with overflow protection, returning null on overflow
safe_negate Negate a numeric value with overflow protection, returning null on overflow
safe_subtract Perform subtraction with overflow protection, returning null on overflow
sec Calculate the secant of a numeric value specified in radians
sha1  
sha256  
sha512  
sign Determine the sign of a numeric value (-1, 0, or 1)
sin Calculate the sine of a numeric value specified in radians
split  
sqrt Calculate the square root of a numeric value
stddev_population (comp) Compute the population standard deviation of a numeric field within the comp stage
stddev_population (windowcomp) Compute the population standard deviation of a numeric field over a window of rows within the windowcomp stage
stddev_sample (comp) Compute the sample standard deviation of a numeric field within the comp stage
stddev_sample (windowcomp) Compute the sample standard deviation of a numeric field over a window of rows within the windowcomp stage
string_count  
subtract  
sum (comp) Compute the sum of a numeric field within the comp stage
sum (windowcomp) Compute the sum of a numeric field over a window of rows within the windowcomp stage
tan Calculate the tangent of a numeric value specified in radians
time_frame_end  
timestamp_diff  
timestamp_seconds  
to_boolean  
to_epoch  
to_float  
to_integer  
to_json_string  
to_number  
to_string  
to_timestamp  
trim  
trunc Truncate a numeric value to a specified number of decimal places
uppercase  
values Collect all distinct values of a field and return them as an array within the comp stage
var Compute the variance of a numeric field within the comp stage
wildcard_match  

Stages

Stage Description
alter  
arrayexpand  
bin  
call  
comp  
config  
dataset  
dedup  
fields  
filter  
iploc  
join  
limit  
presets  
replacenull  
search  
sort  
tag  
target  
top  
transaction  
union  
view  
windowcomp  

Reference overview

Cortex XQL (Cortex Query Language) is the query language for threat hunting, investigation, and analytics across security data in Cortex.

XQL queries are built as pipelines. Stages connect with the pipe (|) operator. Functions run inside those stages to transform, filter, and analyze data.

This reference is organized into three main sections.

  • Functions – Built-in functions for data manipulation, type conversion, string operations, math, bitwise operations, and more.
  • XQL JSON Functions Reference – Reference for json_extract, json_extract_scalar, json_extract_array, and json_extract_scalar_array.
  • Stages – Pipeline stages that define query structure and flow.
  • Browse the reference – Quick links to the main indexes and section entry points.

Browse the reference

Use this page for quick access to the main XQL reference sections.

Start here

Functions

Stages

Functions

XQL functions run inside stages and return a value.

Use this section to:

Functions overview

XQL functions run inside stages to transform, compare, and aggregate data.

They cover data manipulation, type conversion, string operations, math, arrays, JSON parsing, and more.

Functions list

The following table lists all available XQL functions:

Function Description
acos Calculate the inverse cosine (arccosine) of a numerical expression
add Calculate the sum of two numbers
approx_count Return an approximate count of distinct values
approx_quantiles Return approximate quantile boundaries
approx_top Return the approximate top values
asin Calculate the inverse sine (arcsine) of a numerical expression
array_all Check if all elements in an array match a condition
array_any Check if any element in an array matches a condition
array_length Return the number of elements in an array
arrayconcat Concatenate two arrays
arraycreate Create an array from a list of values
arraydistinct Return an array with duplicate values removed
arrayfilter Filter elements of an array based on a condition
arrayindex Return the element at a specified index in an array
arrayindexof Return the index of a value in an array
arraymap Apply a function to each element of an array
arraymerge Merge multiple arrays into one
arrayrange Create an array of sequential integers
arraystring Convert an array to a string
avg (with comp stage) Calculate the average value using the comp stage
avg (with windowcomp stage) Calculate the average value using the windowcomp stage
bitwise_and Perform a bitwise AND operation between two integer values
bitwise_or Perform a bitwise OR operation between two integer values
bitwise_sleft Perform a bitwise left shift operation on an integer value
bitwise_sright Perform a bitwise right shift operation on an integer value
bitwise_xor Perform a bitwise exclusive OR (XOR) operation between two integer values
cbrt Calculate the cube root of a numeric value
ceil Round a number up to the nearest integer
coalesce Return the first non-null value from a list
concat Concatenate two or more strings
convert_from_base_64 Decode a Base64-encoded string
convert_to_base_64 Encode a string to Base64
cos Calculate the cosine of a numeric value specified in radians
cosine_distance Calculate the cosine distance between two numeric vectors
cot Calculate the cotangent of a numeric value specified in radians
count (with comp stage) Count values using the comp stage
count (with windowcomp stage) Count values using the windowcomp stage
count_distinct Count distinct values
csc Calculate the cosecant of a numeric value specified in radians
current_time Return the current timestamp
date_floor Round a timestamp down to a specified time unit
divide Divide one number by another
earliest Return the earliest timestamp value
euclidean_distance Calculate the Euclidean distance between two numeric vectors
exp Calculate the value of e raised to the power of a numeric value
extract_time Extract a time component from a timestamp
extract_url_host Extract the host from a URL
extract_url_pub_suffix Extract the public suffix from a URL
extract_url_registered_domain Extract the registered domain from a URL
first Return the first value in a group
first_value Return the first value in a window
floor Round a number down to the nearest integer
format_string Format a string using a template
format_timestamp Format a timestamp as a string
greatest Return the largest value from a list of expressions
hierarchy_match Check whether an asset belongs to an organizational hierarchy node
if Return one of two values based on a condition
incidr Check if an IPv4 address is within a CIDR range
incidr6 Check if an IPv6 address is within a CIDR range
incidrlist Check if an IP address is within a list of CIDR ranges
int_to_ip Convert an integer to an IP address string
ip_to_int Convert an IP address string to an integer
is_ipv4 Check if a value is a valid IPv4 address
is_ipv6 Check if a value is a valid IPv6 address
is_known_private_ipv4 Check if an IPv4 address is a known private address
is_known_private_ipv6 Check if an IPv6 address is a known private address
json_extract Extract a value from a JSON string
json_extract_array Extract an array from a JSON string
json_extract_scalar Extract a scalar value from a JSON string
json_extract_scalar_array Extract a scalar array from a JSON string
json_path_extract Extract a value from a JSON string using a JSONPath expression
json_functions_reference Decide which JSON functions to use
lag Return the value of a field from a previous row in a window
last Return the last value in a group
last_value Return the last value in a window
latest Return the latest timestamp value
least Return the smallest value from a list of expressions
len Return the length of a string or array
list (with comp stage) Collect all values of a field and return them as an array within the comp stage
ln Calculate the natural logarithm (base e) of a numeric value
log Calculate the logarithm of a numeric value with a specified base
log10 Calculate the base-10 logarithm of a numeric value
lowercase Convert a string to lowercase
ltrim Remove leading whitespace from a string
max (with comp stage) Return the maximum value of a field within the comp stage
max (with windowcomp stage) Compute the maximum value of a field over a window of rows within the windowcomp stage
md5 Calculate the MD5 hash of a string
median (with comp stage) Return the median value of a numeric field within the comp stage
median (with windowcomp stage) Compute the median value of a numeric field over a window of rows within the windowcomp stage
min (with comp stage) Return the minimum value of a field within the comp stage
min (with windowcomp stage) Compute the minimum value of a field over a window of rows within the windowcomp stage
mod Calculate the remainder (modulus) of the division of two numeric values
multiply Multiply two numbers
object_create Create a JSON object from key-value pairs
object_merge Merge two JSON objects
parse_epoch Parse an epoch timestamp
parse_timestamp Parse a timestamp string
pow Raise a number to a power
power Raise a number to the power of another number (alias for pow)
rand Generate a pseudo-random floating-point number between 0 and 1
range_bucket Determine which bucket a numeric value falls into given an array of boundaries
rank (with windowcomp stage) Assign a rank to each row within a partition in the windowcomp stage
regexcapture Extract substrings using a regular expression
regextract Extract a substring from a field value using a regular expression pattern
replace Replace occurrences of a substring
replex Replace substrings using a regular expression
round Round a number to a specified number of decimal places
row_number (with windowcomp stage) Assign a unique sequential integer to each row within a partition in the windowcomp stage
rtrim Remove trailing whitespace from a string
safe_add Perform addition with overflow protection, returning null on overflow
safe_divide Perform division with error protection, returning null on division by zero
safe_multiply Perform multiplication with overflow protection, returning null on overflow
safe_negate Negate a numeric value with overflow protection, returning null on overflow
safe_subtract Perform subtraction with overflow protection, returning null on overflow
sec Calculate the secant of a numeric value specified in radians
sha1 Calculate the SHA-1 hash of a string
sha256 Calculate the SHA-256 hash of a string
sha512 Calculate the SHA-512 hash of a string
sign Determine the sign of a numeric value (-1, 0, or 1)
sin Calculate the sine of a numeric value specified in radians
split Split a string into an array
sqrt Calculate the square root of a numeric value
stddev_population (with comp stage) Compute the population standard deviation of a numeric field within the comp stage
stddev_population (with windowcomp stage) Compute the population standard deviation of a numeric field over a window of rows within the windowcomp stage
stddev_sample (with comp stage) Compute the sample standard deviation of a numeric field within the comp stage
stddev_sample (with windowcomp stage) Compute the sample standard deviation of a numeric field over a window of rows within the windowcomp stage
string_count Count occurrences of a substring in a string
subtract Subtract one number from another
sum (with comp stage) Compute the sum of a numeric field within the comp stage
sum (with windowcomp stage) Compute the sum of a numeric field over a window of rows within the windowcomp stage
tan Calculate the tangent of a numeric value specified in radians
time_frame_end Return the end of a time frame
timestamp_diff Calculate the difference between two timestamps
timestamp_seconds Convert a timestamp to seconds
to_boolean Convert a value to a boolean
to_epoch Convert a timestamp to an epoch value
to_float Convert a value to a float
to_integer Convert a value to an integer
to_json_string Convert a value to a JSON string
to_number Convert a value to a number
to_string Convert a value to a string
to_timestamp Convert a value to a timestamp
trim Remove leading and trailing whitespace from a string
trunc Truncate a numeric value to a specified number of decimal places
uppercase Convert a string to uppercase
values Collect all distinct values of a field and return them as an array within the comp stage
var Compute the variance of a numeric field within the comp stage
wildcard_match Check if a string matches a wildcard pattern

acos

Use the acos() function to calculate the principal value of the inverse cosine (arccosine) of a numerical expression.

Syntax

acos(<numeric_expression>)

Parameters

Name Type Required Description
numeric_expression integer, float Yes The numerical value for which to calculate the arccosine. The value must be within the range of -1 to 1 (inclusive).

Returns

Type: float

Description: The acos() function returns the principal value of the arccosine of the input in radians. The resulting value is within the range [0, π].

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The input numeric_expression must fall within the closed interval [-1, 1].
  • Out of Range Behavior: If the input is outside the range of -1 to 1, the function returns NaN (Not a Number) or null.
  • Null Handling: If the input expression is null, the function returns null.
  • Radians to Degrees: The result is provided in radians. To convert the result to degrees, multiply the return value by 180 / PI().
  • Common Use Cases: This function is typically utilized in the alter stage for geometric calculations, spatial analysis, or normalizing data vectors.

Examples

Example 1: Calculate arccosine of a literal value

Goal: Calculate the inverse cosine for specific numerical literals to see the radian results.

XQL Code:

dataset = xdr_data
| limit 1
| alter result1 = acos(1), result2 = acos(-1), result3 = acos(0)
| fields result1, result2, result3

Explanation: You use acos() on three different literal values. The function calculates the principal value of the inverse cosine for each. The results are in radians.

Output:

RESULT1 RESULT2 RESULT3
0.0 3.14159... 1.5708...
     

Example 2: Calculate arccosine from a field value

Goal: Calculate the arccosine of values stored in a specific field, ensuring they are within the valid mathematical range.

XQL Code:

dataset = sample_xql_raw
| filter duration_seconds >= -1 and duration_seconds <= 1
| alter arc_cos_val = acos(duration_seconds)
| fields event_id, duration_seconds, arc_cos_val
| limit 3

Explanation: This query first filters the dataset to ensure duration_seconds contains only values between -1 and 1, then computes the arccosine for each and stores it in arc_cos_val.

add

Use the add() function to calculate the sum of two numbers.

Syntax

add (<value_1>, <value_2>)

Parameters

Name Type Required Description
value_1 integer, float, string Yes The first numeric value or field.
value_2 integer, float, string Yes The second numeric value or field.

Returns

The add() function returns the mathematical sum of the two input parameters.

Usage notes

  • The function operates on numbers, supporting integer literals and floating-point numbers.
  • The function also accepts field values that represent numbers, even if those values are stored as a string data type (for example, an integer stored as text).
  • The function is typically used within the alter stage to create or modify fields based on calculated values.

Examples

Example 1: Adding an integer field and a literal integer

Goal: Add a fixed numerical value to an existing integer field (event_id) to create a new calculated field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_event_id = add(event_id, 100) // Adds 100 to the 'event_id' field 
| fields event_id, new_event_id 
| limit 3

Explanation: This query adds 100 to the event_id of each record, storing the result in a new field called new_event_id. For event_id 101, new_event_id becomes 201.

Output:

event_id new_event_id
101 201
102 202
103 203

Example 2: Adding two integer literal values

Goal: Perform addition directly on two static integer values to create a constant new field for each record.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter sum_of_literals = add(500, 25) // Adds two literal integer values 
| fields event_id, sum_of_literals 
| limit 3

Explanation: This query adds the literal integers 500 and 25, producing a constant sum_of_literals value of 525 for each record.

Output:

event_id sum_of_literals
101 525
102 525
103 525

Example 3: Adding a floating-point field and a literal float

Goal: Operate on a floating-point number field and a literal floating-point value.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter increased_duration = add(duration_seconds, 0.5) // Adds 0.5 to 'duration_seconds' 
| fields event_id, duration_seconds, increased_duration 
| limit 3

Explanation: The duration_seconds field (a float) is increased by 0.5 for each record. For event_id 101 (duration 1.5), increased_duration becomes 2.0.

Output:

event_id duration_seconds increased_duration
101 1.5 2.0
102 0.8 1.3
103 10.2 10.7

Example 4: Adding an integer field and a negative literal

Goal: Handle a negative literal to effectively perform subtraction.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter adjusted_id = add(event_id, -10) // Subtracts 10 from 'event_id' 
| fields event_id, adjusted_id 
| limit 3

Explanation: This query subtracts 10 from the event_id of each record. For event_id 101, adjusted_id becomes 91.

Output:

event_id adjusted_id
101 91
102 92
103 93

Example 5: Adding a number extracted as string and a literal

Goal: Use add() with a numeric value extracted from a JSON string field, which is first converted to a number.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter status_code_str = simple_json_data -> code // Extract 'code' as string 
| alter status_code_num = to_number(status_code_str) // Convert string to number 
| alter new_code_value = add(status_code_num, 50) // Add 50 to the numeric code 
| fields event_id, simple_json_data, new_code_value 
| limit 3

Explanation: For event_id 101, the code "200" is extracted as a string, converted to a number, and then 50 is added, resulting in new_code_value of 250. For event_id 102, $.code is NULL, so new_code_value will also be NULL.

Output:

event_id simple_json_data new_code_value
101 "{"status": "ok", "code": 200} " 250
102 "{"status": "fail", "error": "access_denied"} " NULL
103 "{"connection_id": "CONN-001", "protocol": "TCP"} " NULL

Example 6: Adding an element from an array field and a literal

Goal: Access an element from an array field and add a literal integer to it.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_numeric_code = arrayindex(numeric_codes, 0) // Get the first element of the array 
| alter increased_first_code = add(first_numeric_code, 10) // Add 10 to the first element 
| fields event_id, numeric_codes, increased_first_code 
| limit 3

Explanation: For event_id 101, the first element of numeric_codes (13) is extracted, and 10 is added to it, resulting in increased_first_code of 23.

Output:

event_id numeric_codes increased_first_code
101 "[13, -47, 29, 82, -15] " 23
102 "[-21, 56, 13, -88, 42] " -11
103 "[90, -33, 7, 51, -62, 18] " 100

approx_count

Use the approx_count() function to calculate an approximate count of distinct values in a specified field.

Syntax

approx_count(<field>)

Parameters

Name Type Required Description
field string Yes The name of the field for which you want to count distinct values.

Returns

The approx_count() function returns a single approximate integer value representing the number of distinct values found in the specified field.

Usage notes

  • The approx_count() function is an approximate aggregate function. The function is designed to be more scalable in terms of memory usage and processing time compared to exact aggregate functions like count_distinct(), especially for large datasets.
  • This function must be used with the comp stage.
  • You can use the by clause in the comp stage to partition the data into groups, allowing approx_count() to compute the approximate distinct count independently for each group.
  • You can use the addrawdata = true option in the comp stage to include a column listing the raw data events that contributed to the aggregate result.

Examples

Example 1: Calculate approximate distinct count across the entire dataset

Goal: Calculate the approximate number of unique event_description values for all records in the dataset.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp approx_count(event_description) as unique_event_descriptions

Explanation: This query computes the approximate number of unique event_description values present in the sample_xql_raw dataset and names the resulting field unique_event_descriptions.

Output:

unique_event_descriptions
10

Example 2: Calculate approximate distinct count grouped by another field

Goal: Calculate the approximate number of unique dst_domain values separately for successful and unsuccessful events.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp approx_count(dst_domain) as unique_domains_by_status by is_successful

Explanation: The query groups records by their is_successful status and then calculates the approximate number of unique dst_domain values for each group, presenting the counts in the unique_domains_by_status field.

Output:

is_successful unique_domains_by_status
true 7
false 3

Example 3: Calculate approximate distinct count with raw data inclusion

Goal: Calculate the approximate distinct count of event_description grouped by is_successful and include the raw events that contributed to the calculation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp approx_count(event_description) as unique_descriptions_by_status by is_successful addrawdata = true as raw_events_for_distinct_count

Explanation: This query calculates the approximate distinct count of event_description grouped by is_successful. Additionally, addrawdata = true generates a new column named raw_events_for_distinct_count, which contains a JSON representation of the raw events that contributed to each computed approximate distinct count.

Output:

is_successful unique_descriptions_by_status raw_events_for_distinct_count
true 7 [{"event_id": 101, "event_description": "User login successful", ...}, {"event_id": 103, "event_description": "Network connection established", ...}, ...]
false 3 [{"event_id": 102, "event_description": "File access attempt", ...}, {"event_id": 106, "event_description": "Unauthorized access detected", ...}, ...]

approx_quantiles

Use the approx_quantiles() function to calculate and return approximate boundaries for a specified field, producing an array of values that define the quantiles.

Syntax

approx_quantiles(<field>, <number>, <distinct>)

Parameters

Name Type Required Description
field string, integer, float Yes The name of the field for which to calculate approximate quantiles.
number integer Yes An integer specifying the number of quantiles to compute. The output array will contain <number> + 1 elements.
distinct boolean No Determines whether to consider only distinct values (true) or all values (false). If omitted, the default is false.

Returns

The approx_quantiles() function returns a single array containing <number> + 1 approximate values representing the boundaries of the quantiles. The first element represents the approximate minimum, and the last element represents the approximate maximum.

Usage notes

  • The approx_quantiles() function is an approximate aggregate function designed to produce approximate results, instead of exact results used with regular aggregate functions, which is more scalable in terms of memory usage and time.
  • This function must always be used with the comp stage.
  • You can use the by clause with comp to calculate quantiles independently for different groups.
  • You can use the addrawdata = true option to include a column listing the raw data events that contributed to the calculation. When you include raw data events, the query runs for up to 50 fields that you define and displays up to 100 events.

Examples

Example 1: Calculate approximate quantiles across the entire dataset

Goal: Calculate 3 approximate quantiles (4 boundaries) for the duration_seconds field across all records, considering all values (non-distinct).

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp approx_quantiles(duration_seconds, 3) as approx_duration_quantiles

Explanation: This query calculates 3 quantiles for the duration_seconds field. The output is an array containing the approximate minimum, the 25th percentile, the 50th percentile (median), the 75th percentile, and the approximate maximum.

Output:

approx_duration_quantiles
[0.05, 1.0, 3.55, 9.0, 60.0]

Example 2: Calculate approximate quantiles grouped by field

Goal: Calculate 2 approximate quantiles (3 boundaries: min, median, max) for duration_seconds, grouped by the is_successful status.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp approx_quantiles(duration_seconds, 2) as approx_duration_quantiles by is_successful

Explanation: The query groups records by is_successful and calculates the approximate boundaries (min, median, max) of the duration_seconds values for each group.

Output:

is_successful approx_duration_quantiles
true [0.1, 7.8, 60.0]
false [0.05, 0.8, 2.1]

Example 3: Calculate approximate quantiles for distinct values

Goal: Calculate 1 approximate quantile (2 boundaries: min, max) for event_description, considering only distinct values.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp approx_quantiles(event_description, 1, true) as approx_event_description_range

Explanation: This query sets the third parameter to true, instructing the function to consider only unique event_description values. The query finds the alphabetically first and last descriptions.

Output:

approx_event_description_range
["API request throttled", "User login successful"]

approx_top

Use the approx_top() function to return the most frequently occurring elements or highest-sum elements for a specified field. This approximate aggregate function produces results that are highly scalable in terms of memory usage and time compared to exact results from regular aggregate functions.

Syntax

comp approx_top(<string field>, <number>) [as <alias>[] [by <field1>[,<field2>...[][] [addrawdata = true|false [as <target field>[][]
comp approx_top(<string field>, <number>, <weight string field>) [as <alias>[] [by <field1>[,<field2>...[][][addrawdata = true|false [as <target field>[][]

Parameters

Name Type Required Description
string field string Yes The field for which to find the top elements.
number integer No An integer specifying the number of top elements to return. If omitted, it defaults to up to 10 elements.
weight string field numeric No A numeric field used to calculate the sum for each unique value in the first specified field, causing the function to return approximate sums instead of counts.

Returns

The approx_top() function returns a single array containing up to the specified number of JSON objects (structs). For the count variant, each struct will have "value" (the unique field value) and "count" (its approximate number of occurrences) keys. For the sum variant, each struct will have "value" and "sum" keys, representing the approximate sum calculated using the weight string field.

Usage Notes

  • The comp stage must always precede an approximate aggregate function like approx_top().
  • You can use the optional by clause to group rows based on one or more specified fields, which allows approx_top() to compute the approximate top elements independently for each group.
  • When you set the optional addrawdata parameter to true, the query processes up to 50 defined fields and includes a raw_data column displaying up to 100 raw data events that contributed to the aggregate result.
  • New columns created by the comp stage are typically added as the last columns in the result set, and any other fields not explicitly included in the by clause or as part of a calculated column is removed from the result set, including the _time system field.

Examples

Example 1: Calculating approximate top counts (no weight field)

Goal: Calculate the top 2 approximate counts of is_successful statuses across the entire dataset.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| comp approx_top(is_successful, 2) as top_successful_statuses 

Explanation: The code identifies the top 2 most frequent values in the is_successful field. The result is an array of JSON objects, each with a "value" and a "count".

Output:

top_successful_statuses
[{"value": true, "count": 7}, {"value": false, "count": 3}[]

Example 2: Calculating approximate top sums (with weight field)

Goal: Calculate the top 2 dst_domain values by the sum of their duration_seconds across the entire dataset.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| comp approx_top(dst_domain, 2, duration_seconds) as top_domains_by_duration_sum

Explanation: The code computes the sum of duration_seconds for each unique dst_domain and then returns the top 2 dst_domain values based on these sums.

Output:

top_domains_by_duration_sum
[{"value": "www.mongodb.com", "sum": 60.0}, {"value": "downloads.teamviewer.com", "sum": 15.3}[]

Example 3: Calculating approximate top sums grouped by another field

Goal: Calculate the top 1 dst_domain by the sum of duration_seconds separately for is_successful = true and is_successful = false events.

XQL Code:


config timeframe = 1d   
| dataset = sample_xql_raw   
| comp approx_top(dst_domain, 1, duration_seconds) as top_domain_durations_by_status by is_successful

Explanation: Groups records by their is_successful status and calculates the single dst_domain with the highest sum of duration_seconds for each group.

Output:

is_successful top_domain_durations_by_status
true [{"value": "www.mongodb.com", "sum": 60.0}[]
false [{"value": "sharepoint.microsoft.com", "sum": 2.1}[]

Example 4: Calculating approximate top counts with raw data inclusion

Goal: Include the raw events that contribute to the approximate top count calculation by using the addrawdata = true option.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| comp approx_top(is_successful, 1) addrawdata = true as raw_data_for_top_is_successful

Explanation: The code computes the top 1 most frequent is_successful value and automatically generates a new column named raw_data_for_top_is_successful containing a JSON representation of the raw events that contributed to this result.

Output:

top_successful_statuses raw_data_for_top_is_successful
[{"value": true, "count": 7}[] [{"event_id": 101, "is_successful": true, ...}, {"event_id": 103, "is_successful": true, ...}, ...[] (up to 100 events)

array_all

Use the array_all() function to determine if all elements within a specified array satisfy a defined boolean condition.

Syntax

array_all (<array>, "@element"<operator>"<array_element>")

Parameters

Name Type Required Description
array array Yes The array field to evaluate.
@element keyword Yes A special keyword representing each individual element within the array during evaluation.
operator string Yes Any supported XQL comparison operator (for example, =, !=, >, <, >=, <=).
array_element string, integer, float, boolean Yes The value or condition against which each array element is compared.

Returns

The array_all() function returns a boolean value (true or false). The function returns true only if every single element in the array meets the condition; otherwise, the function returns false.

Usage notes

  • The function implements a strict "AND" operation across all elements. If even one element fails the condition, the entire function returns false.
  • The array_all() function will return false if run against an empty array.
  • The function is typically used within the alter or filter stages for data transformation or narrowing down results.

Examples

Example 1: Checking if all string tags are a specific value

Goal: Verify if every tag in the string_tags array is exactly "security".

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter all_tags_are_security = array_all(string_tags, "@element" = "security") 
| fields event_id, string_tags, all_tags_are_security 
| limit 5

Explanation: For event_id 101, the array ["security", "login"] returns false because "login" is not "security". For event_id 104, [] (an empty array) returns false.

Output:

EVENT_ID STRING_TAGS ALL_TAGS_ARE_SECURITY
101 ["security", "login"] false
102 ["filesystem", "critical"] false
103 ["network", "cloud"] false
104 [] false
105 ["data_ops"] false

Example 2: Checking if all numeric codes are greater than zero

Goal: Evaluate if every number in the numeric_codes array is greater than zero.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter all_codes_positive = array_all(numeric_codes, "@element" > 0) 
| fields event_id, numeric_codes, all_codes_positive 
| limit 5

Explanation: For event_id 101, the array [13, -47, 29, 82, -15] returns false because elements like -47 and -15 are not greater than zero. For event_id 104, [] (an empty array) returns false.

Output:

EVENT_ID NUMERIC_CODES ALL_CODES_POSITIVE
101 [13, -47, 29, 82, -15] false
102 [-21, 56, 13, -88, 42] false
103 [90, -33, 7, 51, -62, 18] false
104 [] false
105 [77, -9, 35, -47, 61] false

Example 3: Checking if all string tags are NOT a specific value

Goal: Check if all tags in string_tags are not equal to "security" using the != operator.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter none_are_security = array_all(string_tags, "@element" != "security") 
| fields event_id, string_tags, none_are_security 
| limit 5

Explanation: For event_id 101, the array ["security", "login"] returns false because "security" is present in the array, failing the "not equal to security" condition. For event_id 102, the array ["filesystem", "critical"] returns true because neither "filesystem" nor "critical" are equal to "security".

Output:

EVENT_ID STRING_TAGS NONE_ARE_SECURITY
101 ["security", "login"] false
102 ["filesystem", "critical"] true
103 ["network", "cloud"] true
104 [] false
105 ["data_ops"] true

array_any

Use the array_any() function to determine if at least one element within a specified array satisfies a defined boolean condition. If at least one element meets the condition, the function returns true.

Syntax

array_any (<array>, "@element"<operator>"<array_element>")

Parameters

Name Type Required Description
array array Yes The array field to be evaluated.
condition string Yes A comparison expression enclosed in quotes. The condition must use the special keyword @element to represent the individual item being checked, followed by an operator and the value to compare against.

Returns

The array_any() function returns a boolean value (true or false).

Usage notes

  • The function iterates through the array and applies the condition to each element individually.
  • The function implements a logical "OR" operation across the elements. If any single element satisfies the condition, the function returns true.
  • If the input array is empty, the function returns false.
  • Supported operators within the condition include standard comparison operators such as =, !=, >, <, >=, and <=.
  • This function is typically used within the alter or filter stages to categorize data or narrow down results based on array contents.

Examples

Example 1: Check for specific string value

Goal: Check if the string_tags array contains the specific value "security".

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter any_tag_is_security = array_any(string_tags, "@element" = "security") 
| fields event_id, string_tags, any_tag_is_security 
| limit 6 

Explanation: The query evaluates the string_tags array for each event. If the string "security" is present as any element in the array, any_tag_is_security is set to true.

Output:

EVENT_ID STRING_TAGS ANY_TAG_IS_SECURITY
101 ["security", "login"] true
102 ["filesystem", "critical"] false
103 ["network", "cloud"] false
104 [] false
105 ["data_ops"] false
106 ["security", "attack"] true

Example 2: Check numeric threshold

Goal: Determine if any number within the numeric_codes array is greater than 50.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter any_code_gt_50 = array_any(numeric_codes, "@element" > 50) 
| fields event_id, numeric_codes, any_code_gt_50 
| limit 5 

Explanation: The query checks the numeric_codes array. If at least one number in the array is greater than 50, the result is true. Empty arrays return false.

Output:

EVENT_ID NUMERIC_CODES ANY_CODE_GT_50
101 [13, -47, 29, 82, -15] true
102 [-21, 56, 13, -88, 42] true
103 [90, -33, 7, 51, -62, 18] true
104 [] false
105 [77, -9, 35, -47, 61] true

Example 3: Check inequality

Goal: Determine if at least one tag in the string_tags array is not equal to "security".

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter any_tag_is_not_security = array_any(string_tags, "@element" != "security") 
| fields event_id, string_tags, any_tag_is_not_security 
| limit 6 

Explanation: The query returns true if it finds any element in the array that is not "security". For example, in event 101, even though "security" is present, the presence of "login" (which is != "security") makes the result true.

Output:

EVENT_ID STRING_TAGS ANY_TAG_IS_NOT_SECURITY
101 ["security", "login"] true
102 ["filesystem", "critical"] true
103 ["network", "cloud"] true
104 [] false
105 ["data_ops"] true
106 ["security", "attack"] true

array_length

Use the array_length() function to return the number of elements in an array.

Syntax

array_length (<array>)

Parameters

Name Type Required Description
array array Yes The array field you want to evaluate.

Returns

The array_length() function returns an integer representing the count of elements in the specified array.

Usage notes

  • If the input array is empty, the function returns 0.
  • The function provides a direct count of how many items are present within an array field.
  • This function is typically used within the alter stage to create new fields or modify existing ones by calculating array sizes.
  • The function can also be used in filter stages to narrow down results based on string length.

Examples

Example 1: Calculating the length of a string array

Goal: Determine the number of string elements in an array field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter num_string_tags = array_length(string_tags) 
| fields event_id, string_tags, num_string_tags 
| limit 4

Explanation: For event_id 101, 102, and 103, string_tags contains two elements, so num_string_tags is 2. For event_id 104, string_tags contains one element, so num_string_tags is 1.

Output:

EVENT_ID STRING_TAGS NUM_STRING_TAGS
101 ["security", "login"] 2
102 ["filesystem", "critical"] 2
103 ["network", "cloud"] 2
104 ["monitoring"] 1

Example 2: Calculating the length of a numeric array

Goal: Determine the number of numeric elements in an array field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter num_numeric_codes = array_length(numeric_codes) 
| fields event_id, numeric_codes, num_numeric_codes 
| limit 6

Explanation: For event_id 101, 102, and 105, numeric_codes contains five elements. For event_id 103 and 106, numeric_codes contains six elements. For event_id 104, numeric_codes is an empty array, so num_numeric_codes is 0.

Output:

EVENT_ID NUMERIC_CODES NUM_NUMERIC_CODES
101 [13, -47, 29, 82, -15] 5
102 [-21, 56, 13, -88, 42] 5
103 [90, -33, 7, 51, -62, 18] 6
104 [] 0
105 [77, -9, 35, -47, 61] 5
106 [-12, 24, 68, -59, 37, 80] 6

Example 3: Calculating the length of an array of JSON objects

Goal: Determine the number of JSON objects contained within an array field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter num_json_objects = array_length(array_of_json_objects) 
| fields event_id, array_of_json_objects, num_json_objects 
| limit 4

Explanation: For event_id 101 and 103, array_of_json_objects contains two elements. For event_id 102, array_of_json_objects contains one element. For event_id 104, array_of_json_objects is an empty array, so num_json_objects is 0.

Output:

EVENT_ID ARRAY_OF_JSON_OBJECTS NUM_JSON_OBJECTS
101 [{"action": "read", "file": "doc1.txt"}, {"action": "write", "file": "report.log"}] 2
102 [{"event": "file_open", "path": "/etc/passwd"}] 1
103 [{"conn_type": "outbound", "bytes": 1024}, {"conn_type": "inbound", "bytes": 512}] 2
104 [] 0

Example 4: Using array_length() in a filter to check for non-empty arrays

Goal: Filter events to include only those where a specific array field is not empty.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| alter tags_count = array_length(string_tags)
| filter tags_count > 0
| fields event_id, string_tags, tags_count 
| limit 5

Explanation: This query returns all events where the string_tags array has at least one element. No events with an empty string_tags array are present in this filtered result set.

Output:

EVENT_ID STRING_TAGS TAGS_COUNT
101 ["security", "login"] 2
102 ["filesystem", "critical"] 2
103 ["network", "cloud"] 2
104 ["monitoring"] 1
105 ["data_ops"] 1

Example 5: Using array_length() in a filter to check for a specific array size

Goal: Filter events to include only those where an array field contains a precise number of elements.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| alter codes_count = array_length(numeric_codes)
| filter codes_count = 5 
| fields event_id, numeric_codes, codes_count 
| limit 5

Explanation: Only events with exactly five elements in their numeric_codes array are returned, such as event_id 101, 102, and 105. Events like 103 (length 6) or 104 (length 0) are excluded by the filter.

Output:

EVENT_ID NUMERIC_CODES CODES_COUNT
101 [13, -47, 29, 82, -15] 5
102 [-21, 56, 13, -88, 42] 5
105 [77, -9, 35, -47, 61] 5

arrayconcat

Use the arrayconcat() function to combine the elements of two or more specified arrays into a single, new array.

Syntax

arrayconcat (<array1>, <array2>[, <array3>...])

Parameters

Name Type Required Description
array1 array Yes The first array field whose elements will be combined.
array2 array Yes The second array field whose elements will be combined.
array3... array No Additional array fields to be combined.

Returns

The arrayconcat() function returns a single, new array that contains all elements from the input arrays.

Usage notes

  • All the elements within the input array fields must be of the same data type.
  • The function joins the input arrays sequentially. The elements from array1 come first, followed by elements from array2, and so on, preserving their original order within each array.
  • arrayconcat() simply joins the elements and does not inherently remove duplicate values. If you need a result array with only unique elements, apply the arraydistinct() function after arrayconcat().
  • This function is typically employed within the alter stage to create new fields or modify existing ones by combining array data.

Examples

Example 1: Concatenating two newly created arrays

Goal: Define two new arrays using arraycreate() and then concatenate them to form a single combined array.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_part = arraycreate("componentA", "statusX") // Creates a new array 
| alter second_part = arraycreate("typeB", "severityY")   // Creates another new array 
| alter combined_info = arrayconcat(first_part, second_part) // Concatenates the two new arrays 
| fields event_id, first_part, second_part, combined_info 
| limit 2 

Explanation: For each record, two literal arrays, first_part and second_part, are created. The function then combines the elements of first_part followed by second_part into the combined_info array.

Output:

EVENT_ID FIRST_PART SECOND_PART COMBINED_INFO
101 ["componentA", "statusX"] ["typeB", "severityY"] ["componentA", "statusX", "typeB", "severityY"]
102 ["componentA", "statusX"] ["typeB", "severityY"] ["componentA", "statusX", "typeB", "severityY"]

Example 2: Concatenating an existing array field with a newly created array

Goal: Combine the string_tags array field from the dataset with a new array created using arraycreate().

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_event_tags = arraycreate("audit", "compliance") // Creates a new array of tags 
| alter extended_string_tags = arrayconcat(string_tags, new_event_tags) // Concatenates existing string_tags with the new tags 
| fields event_id, string_tags, new_event_tags, extended_string_tags 
| limit 3 

Explanation: For each event, new_event_tags is created. The existing string_tags are then concatenated with the elements from new_event_tags to form extended_string_tags. For example, event_id 101's tags are expanded to include "audit" and "compliance".

Output:

EVENT_ID STRING_TAGS NEW_EVENT_TAGS EXTENDED_STRING_TAGS
101 ["security", "login"] ["audit", "compliance"] ["security", "login", "audit", "compliance"]
102 ["filesystem", "critical"] ["audit", "compliance"] ["filesystem", "critical", "audit", "compliance"]
103 ["network", "cloud"] ["audit", "compliance"] ["network", "cloud", "audit", "compliance"]

Example 3: Concatenating multiple derived arrays

Goal: Combine portions of the numeric_codes array using arrayrange() and a newly created array, demonstrating concatenation with more than two inputs.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_two_codes = arrayrange(numeric_codes, 0, 2) // Extracts elements at index 0 and 1 
| alter next_two_codes = arrayrange(numeric_codes, 2, 4)   // Extracts elements at index 2 and 3 
| alter additional_codes = arraycreate(999, 1000)        // Creates an array with two new numbers 
| alter all_combined_codes = arrayconcat(first_two_codes, next_two_codes, additional_codes) // Concatenates three arrays 
| fields event_id, numeric_codes, first_two_codes, next_two_codes, additional_codes, all_combined_codes 
| limit 3 

Explanation: arrayrange() extracts specific slices (sub-arrays) from the numeric_codes array. arraycreate() is used to define an additional array. The function then takes these two derived arrays and the additional_codes array, joining all their elements into all_combined_codes.

Output:

EVENT_ID NUMERIC_CODES FIRST_TWO_CODES NEXT_TWO_CODES ADDITIONAL_CODES ALL_COMBINED_CODES
101 [13, -47, 29, 82, -15] [13, -47] [29, 82] [999, 1000] [13, -47, 29, 82, 999, 1000]
102 [-21, 56, 13, -88, 42] [-21, 56] [13, -88] [999, 1000] [-21, 56, 13, -88, 999, 1000]
103 [90, -33, 7, 51, -62, 18] [90, -33] [7, 51] [999, 1000] [90, -33, 7, 51, 999, 1000]

arraycreate

Use the arraycreate() function to return a new array based on the given parameters defined for its elements.

Syntax

arraycreate ("<array_element1>", "<array_element2>",...)

Parameters

Name Type Required Description
<array_element> string, integer, float, boolean Yes The values to include as elements in the new array. Must be enclosed in quotes if they are string literals.

Returns

The arraycreate() function returns a single, new array containing the specified elements.

Usage notes

  • All elements within the array created by arraycreate() must be of the same data type.
  • The function returns the data types exactly as entered and doesn't implicitly convert string representations of numeric or boolean values. For example, "123" remains a string "123" within the array, and isn't converted to a number.
  • If all elements are NULL, the output is an empty array with no elements.

Examples

Example 1: Creating an array of string literals

Goal: Define a new array containing only direct string values.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter custom_tags = arraycreate("alert", "investigation_needed") 
| fields event_id, custom_tags 
| limit 3

Explanation: For each record in sample_xql_raw, a new field named custom_tags is created. This field contains an array with two string elements: "alert" and "investigation_needed".

Output:

EVENT_ID CUSTOM_TAGS
101 ["alert", "investigation_needed"]
102 ["alert", "investigation_needed"]
103 ["alert", "investigation_needed"]

Example 2: Creating an array with literals representing different data types (as strings)

Goal: Handle different types of literal values, specifically strings, numbers, and booleans, by treating them as string parameters.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter mixed_info = arraycreate("version_1.0", "123", "true", "45.67") 
| fields event_id, mixed_info 
| limit 3

Explanation: A mixed_info array is generated for each record. The query demonstrates that arraycreate() can incorporate various literal values into a single array, with all elements being treated as strings, adhering to the rule that all elements must be of the same data type.

Output:

EVENT_ID MIXED_INFO
101 ["version_1.0", "123", "true", "45.67"]
102 ["version_1.0", "123", "true", "45.67"]
103 ["version_1.0", "123", "true", "45.67"]

Example 3: Creating an array by combining literals with existing field values

Goal: Dynamically build an array by including both static literal values and values from existing fields in each record.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_summary = arraycreate(to_string(event_id), event_description, to_string(is_successful)) 
| fields event_id, event_description, is_successful, event_summary 
| limit 3

Explanation: The to_string() function is used to convert the event_id (numeric) and is_successful (boolean) fields into strings, allowing them to be combined with the event_description (string) into a new array called event_summary. Each row will have a unique event_summary array reflecting its own event_id, event_description, and is_successful status.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL EVENT_SUMMARY
101 "User login successful" true ["101", "User login successful", "true"]
102 "File access attempt" false ["102", "File access attempt", "false"]
103 "Network connection established" true ["103", "Network connection established", "true"]

Example 4: Creating an array with derived values from JSON extraction

Goal: Incorporate values that are the result of other XQL functions, specifically json_extract_scalar() for extracting data from JSON fields.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter status_code = json_extract_scalar(simple_json_data, "$.code") 
| alter user_name = json_extract_scalar(nested_json_data, "$.user.name") 
| alter extracted_details = arraycreate(status_code, user_name) 
| fields event_id, simple_json_data, nested_json_data, extracted_details 
| limit 3

Explanation: status_code is created by extracting the code field from simple_json_data. user_name is created by extracting the user.name field from nested_json_data. extracted_details then combines these two (potentially NULL) string values into a new array. If all the input elements are NULL, then the output is an empty array.

Output:

EVENT_ID SIMPLE_JSON_DATA NESTED_JSON_DATA EXTRACTED_DETAILS
101 {"status": "ok", "code": 200} {"user": {"id": "U1", "name": "Alice"}, ...} ["200", "Alice"]
102 {"status": "fail", "error": "access_denied"} {"process": {"name": "cmd.exe", "pid": 1234}, ...} []
103 {"connection_id": "CONN-001", "protocol": "TCP"} {"source": {"ip": "172.16.0.1", "port": 5000}, ...} []

arraydistinct

Use the arraydistinct() function to return a new array containing only the unique values found in the original input array.

Syntax

arraydistinc (<array>)

Parameters

Name Type Required Description
array array Yes The array field from which to extract unique elements.

Returns

The arraydistinct() function returns a new array, where all elements are unique.

Usage notes

  • Only one instance of each value is retained in the resulting array.
  • The function operates on elements as they are typed. For example, if "100" (string) and 100 (number) were distinct elements in the input array, they would be considered distinct in the output.
  • If the input array is empty, arraydistinct() will return an empty array.

Examples

Example 1: Removing duplicate string literals from a newly created array

Goal: Create an array with intentional string duplicates and then process it to retain only unique string values.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter raw_array_with_duplicates = arraycreate("alert", "security", "alert", "investigation_needed", "security") 
| alter distinct_tags = arraydistinct(raw_array_with_duplicates) 
| fields event_id, raw_array_with_duplicates, distinct_tags 
| limit 3 

Explanation: A new field raw_array_with_duplicates is created for each record using arraycreate(), containing repeated string values "alert" and "security". arraydistinct() is then applied to produce distinct_tags, which contains only the unique string values.

Output:

EVENT_ID RAW_ARRAY_WITH_DUPLICATES DISTINCT_TAGS
101 ["alert", "security", "alert", "investigation_needed", "security"] ["alert", "security", "investigation_needed"]
102 ["alert", "security", "alert", "investigation_needed", "security"] ["alert", "security", "investigation_needed"]
103 ["alert", "security", "alert", "investigation_needed", "security"] ["alert", "security", "investigation_needed"]

Example 2: Removing duplicate numeric literals from a newly created array

Goal: Process an array composed of numeric literals, including duplicates, and yield an array with only unique numeric values.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter raw_numbers_with_duplicates = arraycreate(100, 50, 100, 50, 25, 100) 
| alter distinct_numbers = arraydistinct(raw_numbers_with_duplicates) 
| fields event_id, raw_numbers_with_duplicates, distinct_numbers 
| limit 3 

Explanation: raw_numbers_with_duplicates is created as an array of integers with repeated values. arraydistinct() effectively identifies and removes these numerical duplicates, resulting in distinct_numbers containing only the unique integer values.

Output:

EVENT_ID RAW_NUMBERS_WITH_DUPLICATES DISTINCT_NUMBERS
101 [100, 50, 100, 50, 25, 100] [100, 50, 25]
102 [100, 50, 100, 50, 25, 100] [100, 50, 25]
103 [100, 50, 100, 50, 25, 100] [100, 50, 25]

Example 3: Applying arraydistinct() to an existing array field

Goal: Demonstrate how arraydistinct() handles an existing array field that does not contain duplicates within its individual elements.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter original_tags = string_tags 
| alter processed_tags = arraydistinct(original_tags) 
| fields event_id, original_tags, processed_tags 
| limit 3 

Explanation: The original_tags field holds the existing string_tags array. arraydistinct() processes this array to produce processed_tags. In cases where the original array contains no duplicates, the output array will be identical to the input.

Output:

EVENT_ID ORIGINAL_TAGS PROCESSED_TAGS
101 ["security", "login"] ["security", "login"]
102 ["filesystem", "critical"] ["filesystem", "critical"]
103 ["network", "cloud"] ["network", "cloud"]

Example 4: arraydistinct() on an array constructed from mixed field values with duplicates

Goal: Build an array by combining elements from different fields, introducing an explicit duplicate, and then use arraydistinct() to resolve it.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_tag = arrayindex(string_tags, 0) 
| alter combined_values_raw = arraycreate(first_tag, first_tag, to_string(event_id)) 
| alter unique_combined_values = arraydistinct(combined_values_raw) 
| fields event_id, combined_values_raw, unique_combined_values 
| limit 3 

Explanation: combined_values_raw is constructed using string elements (first_tag repeated, and event_id converted to a string), creating a duplicate. arraydistinct() processes this array, removing the repeated first_tag to yield unique_combined_values.

Output:

EVENT_ID COMBINED_VALUES_RAW UNIQUE_COMBINED_VALUES
101 ["security", "security", "101"] ["security", "101"]
102 ["filesystem", "filesystem", "102"] ["filesystem", "102"]
103 ["network", "network", "103"] ["network", "103"]

Example 5: arraydistinct() on a concatenated array with duplicates

Goal: Use arraydistinct() on an array formed by concatenating an existing array with itself, which inherently introduces duplicates.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter duplicate_numeric_codes = numeric_codes 
| alter duplicated_numeric_array = arrayconcat(numeric_codes, duplicate_numeric_codes) 
| alter distinct_concatenated_numeric = arraydistinct(duplicated_numeric_array) 
| fields event_id, duplicate_numeric_codes, duplicated_numeric_array, distinct_concatenated_numeric 
| limit 3 

Explanation: arrayconcat() is used to join the numeric_codes array with itself, creating duplicated_numeric_array. arraydistinct() is then applied, effectively reducing it back to the original set of unique elements in distinct_concatenated_numeric.

Output:

EVENT_ID DUPLICATE_NUMERIC_CODES DUPLICATED_NUMERIC_ARRAY DISTINCT_CONCATENATED_NUMERIC
101 [13, -47, 29, 82, -15] [13, -47, 29, 82, -15, 13, -47, 29, 82, -15] [13, -47, 29, 82, -15]
102 [-21, 56, 13, -88, 42] [-21, 56, 13, -88, 42, -21, 56, 13, -88, 42] [-21, 56, 13, -88, 42]
103 [90, -33, 7, 51, -62, 18] [90, -33, 7, 51, -62, 18, 90, -33, 7, 51, -62, 18] [90, -33, 7, 51, -62, 18]

arrayfilter

Use the arrayfilter() function to create a new array containing only the elements from the original input array that satisfy a specified boolean condition.

Syntax

arrayfilter (<array>, <condition>)

or

arrayfilter (<array>, "@element"<operator>"<array_element>")

Parameters

Name Type Required Description
array array Yes The array field to filter.
@element keyword Yes A special keyword that refers to each individual element within the array during the evaluation of the condition.
operator operator Yes Any supported XQL comparison operator, such as =, !=, >, <, >=, or <=.
array_element string, integer, float, boolean Yes The value or condition against which each array element is compared.

Returns

The arrayfilter() function returns a new array containing only the elements that meet the specified condition.

Usage notes

  • The specified condition is applied individually to every element in the input array.
  • If the input array is empty, the function will return an empty array.
  • This function is commonly used within the alter or filter stages to perform data transformations or to narrow down query results.

Examples

Example 1: Filtering a string array for specific elements

Goal: Filter the string_tags array to include only tags that match the specific value "security".

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter security_tags = arrayfilter(string_tags, "@element" = "security") 
| fields event_id, string_tags, security_tags 
| limit 3 

Explanation: For event_id 101, "security" matches the condition, so it is included in the new array, while "login" is excluded. For events 102 and 103, no elements match, resulting in an empty array.

Output:

EVENT_ID STRING_TAGS SECURITY_TAGS
101 ["security", "login"] ["security"]
102 ["filesystem", "critical"] []
103 ["network", "cloud"] []

Example 2: Filtering a numeric array for values greater than a threshold

Goal: Filter the numeric_codes array to include only numbers greater than 50.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter large_numeric_codes = arrayfilter(numeric_codes, "@element" > 50) 
| fields event_id, numeric_codes, large_numeric_codes 
| limit 3 

Explanation: The function iterates through the numeric_codes array. For event_id 101, only 82 is greater than 50. For 102, only 56 is greater. For 103, both 90 and 51 are retained.

Output:

EVENT_ID NUMERIC_CODES LARGE_NUMERIC_CODES
101 [13, -47, 29, 82, -15] [82]
102 [-21, 56, 13, -88, 42] [56]
103 [90, -33, 7, 51, -62, 18] [90, 51]

Example 3: Filtering a string array for elements not equal to a value

Goal: Filter the string_tags array to include only tags that are not equal to "login".

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter non_login_tags = arrayfilter(string_tags, "@element" != "login") 
| fields event_id, string_tags, non_login_tags 
| limit 3 

Explanation: For event_id 101, "security" is not "login", so it is included, while "login" is excluded. For events 102 and 103, all elements satisfy the condition (are not "login"), so the entire arrays are retained.

Output:

EVENT_ID STRING_TAGS NON_LOGIN_TAGS
101 ["security", "login"] ["security"]
102 ["filesystem", "critical"] ["filesystem", "critical"]
103 ["network", "cloud"] ["network", "cloud"]

Example 4: Filtering an array and checking its length in a filter stage

Goal: Create a new array by filtering string_tags for elements containing the substring "crit", and then filter the dataset to only show records where this new array is not empty.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter critical_event_tags = arrayfilter(string_tags, "@element" contains "crit") 
| filter array_length(critical_event_tags) > 0 
| fields event_id, string_tags, critical_event_tags 
| limit 3 

Explanation: First, critical_event_tags is created containing only tags that include "crit". For event_id 102, this results in ["critical"]. The filter stage then uses array_length() to retain only records where the filtered array has elements.

Output:

EVENT_ID STRING_TAGS CRITICAL_EVENT_TAGS
102 ["filesystem", "critical"] ["critical"]

arrayindex

Use the arrayindex() function to retrieve a single element from a specified array by its numerical position, or index.

Syntax

arrayindex (<array>, <index>)

Parameters

Name Type Required Description
array array Yes The array from which you want to extract an element.
index integer Yes An integer representing the zero-based position of the element you want to retrieve (for example, 0 for the first element, 1 for the second, and so on).

Returns

The arrayindex() function returns a single value, which is the element located at the specified index within the array.

Usage notes

  • Array elements are accessed using a zero-based index, meaning the first element is at index 0, the second at index 1, etc.
  • If the specified index is beyond the array's boundaries or if the array itself is empty, arrayindex() will return NULL for the field and not an empty array.
  • This function supports negative indices (for example, -1 for the last element, -2 for the second to last element).

Examples

Example 1: Extracting the first element (Index 0) from a string array

Goal: Retrieve the first string tag from the string_tags array.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_tag = arrayindex(string_tags, 0) // Extracts the first element 
| fields event_id, string_tags, first_tag 
| limit 3 

Explanation: For each event_id, the function extracts the first string element (index 0) from the string_tags array.

Output:

event_id string_tags first_tag
101 ["security", "login"] "security"
102 ["filesystem", "critical"] "filesystem"
103 ["network", "cloud"] "network"

Example 2: Extracting a middle element (Index 2) from a numeric array

Goal: Retrieve the third numeric code (at index 2) from the numeric_codes array.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter third_numeric_code = arrayindex(numeric_codes, 2) // Extracts the element at index 2 
| fields event_id, numeric_codes, third_numeric_code 
| limit 3 

Explanation: For each event_id, the function extracts the element at index 2 (the third element) from the numeric_codes array.

Output:

event_id numeric_codes third_numeric_code
101 [13, -47, 29, 82, -15] 29
102 [-21, 56, 13, -88, 42] 13
103 [90, -33, 7, 51, -62, 18] 7

Example 3: Handling an empty array

Goal: Demonstrate the behavior when attempting to extract an element from an empty array.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 104 // Focus on the event with an empty array 
| alter first_numeric_code_empty = arrayindex(numeric_codes, 0) // Attempt to extract from empty array 
| fields event_id, numeric_codes, first_numeric_code_empty 
| limit 1 

Explanation: For event_id 104, the numeric_codes array is empty. As expected, the function returns NULL when no element exists at the specified index due to an empty array.

Output:

event_id numeric_codes first_numeric_code_empty
104 [] NULL

Example 4: Handling an out-of-bounds index

Goal: Demonstrate the behavior when the specified index is beyond the actual size of the array.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter out_of_bounds_code = arrayindex(numeric_codes, 100) // Index 100 is far beyond array size 
| fields event_id, numeric_codes, out_of_bounds_code 
| limit 3 

Explanation: For all event_ids, numeric_codes does not have an element at index 100. The function correctly returns NULL when the index is out of bounds.

Output:

event_id numeric_codes out_of_bounds_code
101 [13, -47, 29, 82, -15] NULL
102 [-21, 56, 13, -88, 42] NULL
103 [90, -33, 7, 51, -62, 18] NULL

Example 5: Retrieving the last element (-1 Index)

Goal: Extract the last element from an array using the negative index -1.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter last_numeric_code = arrayindex(numeric_codes, -1) // Extracts the last element using negative index 
| fields event_id, numeric_codes, last_numeric_code 
| limit 3 

Explanation: Using index -1 retrieves the final element of the numeric_codes array for each record.

Output:

event_id numeric_codes last_numeric_code
101 [13, -47, 29, 82, -15] -15
102 [-21, 56, 13, -88, 42] 42
103 [90, -33, 7, 51, -62, 18] 18

Example 6: Retrieving the second to last element (-2 Index)

Goal: Retrieve the second to last element from an array using the negative index -2.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter second_to_last_numeric_code = arrayindex(numeric_codes, -2) // Extracts the second to last element 
| fields event_id, numeric_codes, second_to_last_numeric_code 
| limit 3 

Explanation: Using index -2 retrieves the element immediately preceding the last element in the numeric_codes array.

Output:

event_id numeric_codes second_to_last_numeric_code
101 [13, -47, 29, 82, -15] 82
102 [-21, 56, 13, -88, 42] -88
103 [90, -33, 7, 51, -62, 18] -62

arrayindexof

Use the arrayindexof() function to return the index of the first occurrence of a specified element in an array that satisfies a condition, or 0 if a general boolean condition matches.

Syntax

arrayindexof (<array>, <condition>)
arrayindexof (<array>, "@element"<operator>"<array element>")

Parameters

Name Type Required Description
array array Yes The array field to evaluate.
condition boolean No A boolean expression that evaluates to true or false for the array.
@element keyword No A special keyword representing each individual element within the array during evaluation.
operator operator No Any supported XQL comparison operator, such as =, !=, >, <, >=, <=.
array_element string, integer, float, boolean No The value or condition against which each array element is compared.

Returns

The arrayindexof() function returns an integer (0 or a 0-based index) or NULL.

Usage notes

  • If the condition is a general boolean expression not using @element, the function returns 0 if the array is not empty and the condition is true.
  • If the condition uses @element to check individual array elements, the function returns the 0-based index of the first array element that satisfies the condition.
  • If the input array is empty, arrayindexof() returns NULL.
  • If the condition (general or @element-based) is not met by any element, the function returns NULL.

Examples

Example 1: General condition - checking array length

Goal: Check if the string_tags array contains more than one element using a general condition.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter has_multiple_tags = arrayindexof(string_tags, array_length(string_tags) > 1) 
| fields event_id, string_tags, has_multiple_tags 
| limit 4 

Explanation: The query uses the arrayindexof(<array>, <condition>) variant. For event_id 101, 102, and 103, the length is 2, so the condition matches and returns 0. For event_id 104, the length is 1, so the condition fails and returns NULL.

Output:

EVENT_ID STRING_TAGS HAS_MULTIPLE_TAGS
101 ["security", "login"] 0
102 ["filesystem", "critical"] 0
103 ["network", "cloud"] 0
104 ["monitoring"] NULL

Example 2: Element-specific condition - checking for a specific string value

Goal: Find the index of the first occurrence of the tag "login" in the string_tags array.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_index_of_login_tag = arrayindexof(string_tags, "@element" = "login") 
| fields event_id, string_tags, first_index_of_login_tag 
| limit 5 

Explanation: The query uses the @element keyword to check each item. For event_id 101, "login" is found at index 1, so 1 is returned. For other events where "login" is missing, NULL is returned.

Output:

EVENT_ID STRING_TAGS FIRST_INDEX_OF_LOGIN_TAG
101 ["security", "login"] 1
102 ["filesystem", "critical"] NULL
103 ["network", "cloud"] NULL
104 ["monitoring"] NULL
105 ["data_ops"] NULL

Example 3: Element-specific condition - checking for a numeric value greater than a threshold

Goal: Find the index of the first numeric code in the numeric_codes array that is greater than 50.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_index_of_large_code = arrayindexof(numeric_codes, "@element" > 50) 
| fields event_id, numeric_codes, first_index_of_large_code 
| limit 6 

Explanation: This query searches the entire array and returns the index of the first match. For event_id 101, 82 is the first value > 50 (at index 3). For event_id 102, 56 is the first match (at index 1).

Output:

EVENT_ID NUMERIC_CODES FIRST_INDEX_OF_LARGE_CODE
101 [13, -47, 29, 82, -15] 3
102 [-21, 56, 13, -88, 42] 1
103 [90, -33, 7, 51, -62, 18] 0
104 [] NULL
105 [77, -9, 35, -47, 61] 0
106 [-12, 24, 68, -59, 37, 80] 2

Example 4: Element-specific condition - checking for a value not equal to a specific string

Goal: Find the index of the first element in string_tags that is not "security".

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_index_of_non_security = arrayindexof(string_tags, "@element" != "security") 
| fields event_id, string_tags, first_index_of_non_security 
| limit 5 

Explanation: For event_id 101, "security" is at index 0, but "login" is at index 1 and matches the condition != "security", so 1 is returned. For event_id 102, "filesystem" at index 0 matches, so 0 is returned.

Output:

EVENT_ID STRING_TAGS FIRST_INDEX_OF_NON_SECURITY
101 ["security", "login"] 1
102 ["filesystem", "critical"] 0
103 ["network", "cloud"] 0
104 ["monitoring"] 0
105 ["data_ops"] 0

Example 5: Handling an empty array explicitly

Goal: Demonstrate that arrayindexof() returns NULL when applied to an empty array.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 104 // Focus on the event with an empty array 
| alter index_in_empty_array = arrayindexof(numeric_codes, "@element" = 0) 
| fields event_id, numeric_codes, index_in_empty_array 
| limit 1 

Explanation: For event_id 104, the numeric_codes array is empty. As per the function's definition, if the array is empty, NULL is returned.

Output:

EVENT_ID NUMERIC_CODES INDEX_IN_EMPTY_ARRAY
104 [] NULL

arraymap

Use the arraymap() function to apply a specified callable function to every element of an input array, returning a new array containing the transformed elements.

Syntax

arraymap (<array>, <function()>)

Parameters

Name Type Required Description
array array Yes The array whose elements you want to transform.
function() function Yes The function to apply to each element of the array. Use the special keyword "@element" as a placeholder for the current array element being processed.

Returns

The arraymap() function returns a new array, where each element is the result of applying the specified function to the corresponding element of the original array.

Usage notes

  • The function iterates through each element, applies the defined transformation, and collects the results into a new array.
  • When the function applied within arraymap() needs to reference the current array element being processed, it uses "@element" as a placeholder for that element.
  • arraymap() is typically used within the alter or filter stages for data transformations.

Examples

Example 1: Applying a simple mathematical transformation to a numeric array

Goal: Multiply each element in a numeric array by a specific constant.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter multiplied_codes = arraymap(numeric_codes, multiply("@element", 10))
| fields event_id, numeric_codes, multiplied_codes
| limit 4

Explanation: For each record, arraymap() iterates over the numeric_codes array. The multiply() function takes each "@element" (for example, 13) and multiplies it by 10. The results form the new multiplied_codes array. An empty input array (event_id 104) results in an empty output array.

Output:

EVENT_ID NUMERIC_CODES MULTIPLIED_CODES
101 [13, -47, 29, 82, -15] [130, -470, 290, 820, -150]
102 [-21, 56, 13, -88, 42] [-210, 560, 130, -880, 420]
103 [90, -33, 7, 51, -62, 18] [900, -330, 70, 510, -620, 180]
104 [] []

Example 2: Applying a string transformation (uppercase) to a string array

Goal: Convert all string elements within an array to uppercase.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter uppercase_tags = arraymap(string_tags, uppercase("@element"))
| fields event_id, string_tags, uppercase_tags
| limit 4

Explanation: The arraymap() function processes each element in string_tags (for example, "security"). The uppercase() function converts "@element" to its uppercase equivalent. The transformed elements form the uppercase_tags array.

Output:

EVENT_ID STRING_TAGS UPPERCASE_TAGS
101 ["security", "login"] ["SECURITY", "LOGIN"]
102 ["filesystem", "critical"] ["FILESYSTEM", "CRITICAL"]
103 ["network", "cloud"] ["NETWORK", "CLOUD"]
104 ["monitoring"] ["MONITORING"]

Example 3: Applying a conditional transformation (replacing negative values) to a numeric array

Goal: Replace any negative numbers in an array with 0, while keeping positive numbers unchanged.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter positive_codes = arraymap(numeric_codes, if("@element" < 0, 0, "@element"))
| fields event_id, numeric_codes, positive_codes
| limit 4

Explanation: For each number in numeric_codes, the if() function checks if "@element" is less than 0. If true, it is replaced with 0; otherwise, the original "@element" value is retained.

Output:

EVENT_ID NUMERIC_CODES POSITIVE_CODES
101 [13, -47, 29, 82, -15] [13, 0, 29, 82, 0]
102 [-21, 56, 13, -88, 42] [0, 56, 13, 0, 42]
103 [90, -33, 7, 51, -62, 18] [90, 0, 7, 51, 0, 18]
104 [] []

Example 4: Extracting scalar values from JSON Objects within an array

Goal: Iterate through an array of JSON objects and extract a specific scalar value (for example, action or event) from each object into a new array.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter extracted_actions = arraymap(array_of_json_objects, coalesce(
        json_extract_scalar(to_json_string("@element"), "$.action"),
        json_extract_scalar(to_json_string("@element"), "$.event")
    ))
| fields event_id, array_of_json_objects, extracted_actions
| limit 4

Explanation: arraymap() iterates over each JSON object within array_of_json_objects. For each @element (a JSON object), to_json_string() converts it to a string. json_extract_scalar() then attempts to extract the action field. If not found, it tries to extract the event field. coalesce() returns the first non-null result.

Output:

EVENT_ID ARRAY_OF_JSON_OBJECTS EXTRACTED_ACTIONS
101 [{"action": "read", "file": "doc1.txt"}, {"action": "write", "file": "report.log"}] ["read", "write"]
102 [{"event": "file_open", "path": "/etc/passwd"}] ["file_open"]
103 [{"conn_type": "outbound", "bytes": 1024}, {"conn_type": "inbound", "bytes": 512}] []
104 [] []

arraymerge

Use the arraymerge() function to flatten an input array containing JSON strings representing arrays into a single, merged XQL array.

Syntax

arraymerge (<field>)

Parameters

Name Type Required Description
field array Yes A single array field whose elements are JSON strings, where each JSON string represents an array (for example, ["value1", "value2"]).

Returns

The arraymerge() function returns a new, flattened XQL-native array containing all elements from the inner JSON-string-represented arrays.

Usage notes

  • The function strictly requires an array where each element is a valid JSON string that represents an array.
  • The function flattens the structure, taking an array of arrays (represented as JSON strings) and reducing it to a single-dimensional array.
  • This function is commonly used in conjunction with arraymap() when the internal function of arraymap() produces JSON strings of arrays.

Examples

Example 1: Merging artificially constructed arrays (literal JSON strings)

Goal: Demonstrate the core functionality by creating an array where each element is a JSON string representing an array, and then flattening them into a single array.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter json_array_str_part1 = to_json_string(arraycreate("tag1", "tag2")) // Creates JSON string '["tag1", "tag2"]' 
| alter json_array_str_part2 = to_json_string(arraycreate("valueA", "valueB")) // Creates JSON string '["valueA", "valueB"]' 
| alter input_array_for_merge = arraycreate(json_array_str_part1, json_array_str_part2) // Creates an array of these JSON strings: ['["tag1", "tag2"]', '["valueA", "valueB"]'] 
| alter merged_result = arraymerge(input_array_for_merge) 
| fields event_id, merged_result 
| limit 2 

Explanation: The query first creates two string representations of arrays (json_array_str_part1 and json_array_str_part2) using arraycreate() and to_json_string(). The query then combines these into input_array_for_merge. Finally, arraymerge() extracts the elements from within each JSON string in the input array and combines them into a single, flattened merged_result array.

Output:

EVENT_ID MERGED_RESULT
101 ["tag1", "tag2", "valueA", "valueB"]
102 ["tag1", "tag2", "valueA", "valueB"]

Example 2: Merging scalar values extracted and wrapped into arrays via arraymap()

Goal: Demonstrate how to process an array of JSON objects, extract specific scalar values, wrap them into new conceptual arrays, convert them to JSON strings, and finally flatten the result using arraymerge().

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter mapped_json_arrays_as_strings = arraymap( 
    array_of_json_objects, 
    to_json_string( // Converts the dynamically created array into a JSON string 
        arraycreate( // Creates a new temporary array from extracted scalars 
            to_string(coalesce(json_extract_scalar(to_json_string("@element"), "$.action"), json_extract_scalar(to_json_string("@element"), "$.event"))), // Extracts 'action' or 'event' 
            to_string(coalesce(json_extract_scalar(to_json_string("@element"), "$.file"), json_extract_scalar(to_json_string("@element"), "$.path"), json_extract_scalar(to_json_string("@element"), "$.conn_type"))) // Extracts 'file', 'path', or 'conn_type' 
        ) 
    ) 
) 
| alter flattened_array = arraymerge(mapped_json_arrays_as_strings) 
| fields event_id, array_of_json_objects, flattened_array 
| limit 4 

Explanation: arraymap() iterates over each JSON object in array_of_json_objects. For each element, it extracts specific scalar values (like action, event, file, path) using json_extract_scalar() and wraps them into a temporary array using arraycreate(). to_json_string() converts this temporary array into a JSON string. The result of arraymap() is an array of these JSON strings. Finally, arraymerge() flattens all the inner elements from these strings into a single flattened_array.

Output:

EVENT_ID ARRAY_OF_JSON_OBJECTS FLATTENED_ARRAY
101 [{"action": "read", "file": "doc1.txt"}, {"action": "write", "file": "report.log"}] ["read", "doc1.txt", "write", "report.log"]
102 [{"event": "file_open", "path": "/etc/passwd"}] ["file_open", "/etc/passwd"]
103 [{"conn_type": "outbound", "bytes": 1024}, {"conn_type": "inbound", "bytes": 512}] ["outbound", "inbound"]
104 [] []

Example 2: Merge IP addresses extracted from a nested map

Goal: Create a single consolidated array containing all IPv4 addresses found within the agent_interface_map field. This query extracts the "ipv4" element from each object in the map and merges them into a flattened array.

XQL Code:

dataset = sample_xql_raw
| alter a = arraymerge(arraymap(agent_interface_map, to_json_string(json_extract_array(to_json_string("@element"), "$.ipv4"))))

Explanation:

  1. The query processes the agent_interface_map, which is an array of objects.
  2. arraymap() iterates through each element (@element) in the array.
  3. to_json_string() converts the element to a JSON string so it can be parsed.
  4. json_extract_array(..., "$.ipv4") locates and extracts the IPv4 addresses associated with the "ipv4" key in each object.
  5. arraymerge() takes the resulting nested arrays and flattens them into a single, comprehensive array assigned to the field 'a'.

Output:

agent_interface_map a
[{"ipv4":["10.0.0.1"],"name":"eth0"},{"ipv4":["192.168.1.1"],"name":"wlan0"}] ["10.0.0.1", "192.168.1.1"]
[{"ipv4":["172.16.0.5", "172.16.0.6"],"name":"eth1"}] ["172.16.0.5", "172.16.0.6"]

arrayrange

Use the arrayrange() function to return a new array containing a subset of elements from an original array. The subset is defined by a specified start index (inclusive) and an end index (exclusive).

Syntax

arrayrange (<array>, <start>, <end>)

Parameters

Name Type Required Description
array array Yes The array from which you want to extract a portion.
start integer Yes An integer representing the 0-based index where the new array slice should begin (inclusive).
end integer Yes An integer representing the 0-based index where the new array slice should end (exclusive).

Returns

The arrayrange() function returns a new XQL-native array, which is a slice of the original array.

Usage notes

  • Array indices in XQL are 0-based, meaning the first element is at index 0.
  • The element at the <start> index is included, but the element at the <end> index is not included in the result.
  • If the <end> index is greater than the last element's actual index in the array, the function will return elements from the <start> index up to the end of the original array.
  • If the input array is null or empty, or if the start index is out of bounds or greater than or equal to the end index, arrayrange() will typically return an empty or null array.

Examples

Example 1: Extracting a standard slice from a numeric array

Goal: This example demonstrates extracting a portion from the numeric_codes array using typical start and end indices.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_three_codes = arrayrange(numeric_codes, 0, 3) // Extracts elements from index 0 up to (but not including) index 3 
| fields event_id, numeric_codes, first_three_codes 
| limit 3 

Explanation: The numeric_codes field contains arrays of integers. arrayrange(numeric_codes, 0, 3) creates a new array for each record, taking elements starting from index 0 and stopping before index 3 (i.e., elements at indices 0, 1, and 2).

Output:

event_id numeric_codes first_three_codes
101 [13, -47, 29, 82, -15] [13, -47, 29]
102 [-21, 56, 13, -88, 42] [-21, 56, 13]
103 [90, -33, 7, 51, -62, 18] [90, -33, 7]

Example 2: Extracting a slice where the end index is beyond the array's length

Goal: This example demonstrates the behavior of arrayrange() when the specified end index exceeds the actual number of elements in the array.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter slice_to_end = arrayrange(numeric_codes, 2, 100) // Starts at index 2, goes to actual end if 100 is out of bounds 
| fields event_id, numeric_codes, slice_to_end 
| limit 3 

Explanation: arrayrange(numeric_codes, 2, 100) attempts to extract elements starting from index 2 up to index 99. Because the numeric_codes arrays typically have fewer than 100 elements, the function correctly returns all elements from index 2 to the actual end of each array.

Output:

event_id numeric_codes slice_to_end
101 [13, -47, 29, 82, -15] [29, 82, -15]
102 [-21, 56, 13, -88, 42] [13, -88, 42]
103 [90, -33, 7, 51, -62, 18] [7, 51, -62, 18]

Example 3: Extracting a slice from a string array

Goal: This example illustrates arrayrange() applied to a string array, demonstrating its versatility across different data types.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter middle_tags = arrayrange(string_tags, 1, 3) // Extracts elements from index 1 up to (but not including) index 3 
| fields event_id, string_tags, middle_tags 
| limit 3 

Explanation: The string_tags field contains arrays of strings. arrayrange(string_tags, 1, 3) extracts elements starting from index 1 and stopping before index 3. For the sample data, this often results in just the second element as most string_tags arrays have only two elements (indices 0 and 1).

Output:

event_id string_tags middle_tags
101 ["security", "login"] ["login"]
102 ["filesystem", "critical"] ["critical"]
103 ["network", "cloud"] ["cloud"]

Example 4: Handling an empty array input

Goal: This example demonstrates how arrayrange() behaves when the input array is empty.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 104 // Event ID 104 has an empty 'array_of_json_objects' 
| alter empty_array_slice = arrayrange(array_of_json_objects, 0, 1) // Attempts to extract from an empty array 
| fields event_id, array_of_json_objects, empty_array_slice 
| limit 1 

Explanation: event_id 104 in sample_xql_raw has an empty array_of_json_objects field. When arrayrange() is applied to an empty array, it returns an empty array, indicating that no elements could be extracted from the specified range.

Output:

event_id array_of_json_objects empty_array_slice
104 [] []

Example 5: Using arrayrange() with dynamically created arrays

Goal: This example shows arrayrange() applied to an array that is explicitly constructed within the query using arraycreate().

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter dynamic_numbers = arraycreate(10, 20, 30, 40, 50, 60, 70) // Creates a new array 
| alter dynamic_slice = arrayrange(dynamic_numbers, 2, 5) // Extracts elements from index 2 up to (but not including) index 5 
| fields event_id, dynamic_numbers, dynamic_slice 
| limit 2 

Explanation: dynamic_numbers is a new array created on the fly with a set of integer values. arrayrange(dynamic_numbers, 2, 5) then extracts the elements at indices 2, 3, and 4 from this newly created array, resulting in [30, 40, 50].

Output:

event_id dynamic_numbers dynamic_slice
101 [10, 20, 30, 40, 50, 60, 70] [30, 40, 50]
102 [10, 20, 30, 40, 50, 60, 70] [30, 40, 50]

arraystring

Use the arraystring() function to convert an array into a single string by joining its elements with a specified delimiter.

Syntax

arraystring (<array>, <delimiter>)

Parameters

Name Type Required Description
array array Yes The array field whose elements you want to concatenate into a single string.
delimiter string Yes A string literal that will be used to join the elements of the array.

Returns

The arraystring() function returns a single string where each element of the original array is separated by the specified delimiter.

Usage notes

  • The function requires an existing XQL-native array field as input.
  • The function operates on XQL arrays and implicitly converts elements to strings for concatenation.
  • The specified delimiter is inserted between each element of the original array in the resulting string.
  • If the input array is empty (for example, []), the function returns an empty string ("").
  • This function is typically used within the alter stage to create new fields or modify existing ones, but can also be used in filter stages.

Examples

Example 1: Converting a string array to a string with a comma and space delimiter

Goal: Convert an array of strings into a single string, separating each element with a comma and a space.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter combined_tags = arraystring(string_tags, ", ") 
| fields event_id, string_tags, combined_tags 
| limit 3 

Explanation: The string_tags field contains arrays of strings. The arraystring() function converts each array into a single string, using ", " as the separator between elements.

Output:

EVENT_ID STRING_TAGS COMBINED_TAGS
101 ["security", "login"] "security, login"
102 ["filesystem", "critical"] "filesystem, critical"
103 ["network", "cloud"] "network, cloud"

Example 2: Converting a numeric array to a string with a pipe delimiter

Goal: Join elements of a numeric array into a single string separated by a pipe character.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter numeric_codes_as_strings = arraymap(numeric_codes, to_string("@element")) 
| alter combined_codes = arraystring(numeric_codes_as_strings, " | ") 
| fields event_id, numeric_codes, numeric_codes_as_strings, combined_codes 
| limit 3 

Explanation: The numeric_codes field contains arrays of integers. First, arraymap() iterates through each number and converts it to a string using to_string(), creating a new array numeric_codes_as_strings. Then, arraystring() concatenates the elements of this string array using the specified " | " delimiter.

Output:

EVENT_ID NUMERIC_CODES NUMERIC_CODES_AS_STRINGS COMBINED_CODES
101 [13, -47, 29, 82, -15] ["13", "-47", "29", "82", "-15"] 13
102 [-21, 56, 13, -88, 42] ["-21", "56", "13", "-88", "42"] -21
103 [90, -33, 7, 51, -62, 18] ["90", "-33", "7", "51", "-62", "18"] 90

Example 3: Handling an empty array input

Goal: Demonstrate the behavior of the function when the input array is empty.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 104 
| alter empty_array_to_string = arraystring(array_of_json_objects, "-") 
| fields event_id, array_of_json_objects, empty_array_to_string 
| limit 1 

Explanation: When arraystring() is applied to an empty array ([]), it returns an empty string (""), as there are no elements to join.

Output:

EVENT_ID ARRAY_OF_JSON_OBJECTS EMPTY_ARRAY_TO_STRING
104 [] ""

Example 4: Converting a dynamically sliced array to a string

Goal: Apply the function to a portion of a numeric array created using arrayrange.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_two_numeric_codes = arrayrange(numeric_codes, 0, 2) 
| alter sliced_codes_as_strings = arraymap(first_two_numeric_codes, to_string("@element")) 
| alter sliced_codes_string = arraystring(sliced_codes_as_strings, ", ") 
| fields event_id, numeric_codes, first_two_numeric_codes, sliced_codes_as_strings, sliced_codes_string 
| limit 3 

Explanation: First, arrayrange(numeric_codes, 0, 2) extracts the first two elements of numeric_codes. Then, arraymap() converts these numeric elements to strings. Finally, arraystring() concatenates these string elements using ", " as the delimiter.

Output:

EVENT_ID NUMERIC_CODES FIRST_TWO_NUMERIC_CODES SLICED_CODES_AS_STRINGS SLICED_CODES_STRING
101 [13, -47, 29, 82, -15] [13, -47] ["13", "-47"] "13, -47"
102 [-21, 56, 13, -88, 42] [-21, 56] ["-21", "56"] "-21, 56"
103 [90, -33, 7, 51, -62, 18] [90, -33] ["90", "-33"] "90, -33"

Example 5: Using arraystring() in a filter stage

Goal: Filter records by creating a string from an array and checking for a specific substring.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter combined_tags_for_filter = arraystring(string_tags, "-") 
| filter combined_tags_for_filter contains "security" 
| fields event_id, string_tags, combined_tags_for_filter 
| limit 3 

Explanation: The combined_tags_for_filter field is created by joining string_tags elements with a hyphen. The filter stage then checks if this new string field contains the substring "security". Only Event IDs containing "security" in their tags are returned.

Output:

EVENT_ID STRING_TAGS COMBINED_TAGS_FOR_FILTER
101 ["security", "login"] "security-login"
106 ["security", "attack"] "security-attack"

Example 6: Converting application transitions to a delimited string and deduplicating

Goal: Retrieve non-null application ID transitions, convert the transition arrays into strings delimited by " : ", and remove duplicate transition strings based on the insertion time.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| fields action_app_id_transitions  as aait 
| alter transitions_string = arraystring(aait, " : ") 
| dedup transitions_string by asc _time 
| filter aait != null

Explanation: The query first filters the sample_xql_raw dataset to include only records where action_app_id_transitions contains data. The query renames the field to aait for brevity. The arraystring() function then takes each array of transitions and joins the elements into a single string using " : " as the separator. Finally, the dedup stage ensures that only unique transition strings are retained, ordered by the timestamp.

Output:

aait transitions_string
["App_1", "App_2", "App_3"] "App_1 : App_2 : App_3"
["App_A", "App_B"] "App_A : App_B"
["App_1", "App_5"] "App_1 : App_5"

asin

Use the asin() function to calculate the principal value of the inverse sine (arcsine) of a numerical expression.

Syntax

asin(<numeric_expression>)

Parameters

Name Type Required Description
numeric_expression integer, float Yes The numerical value for which to calculate the arcsine. The value must be within the range of -1 to 1 (inclusive).

Returns

Type: float

Description: The asin() function returns the principal value of the arcsine of the input in radians. The resulting value is within the range [-π/2, π/2].

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The input numeric_expression must fall within the closed interval [-1, 1].
  • Out of Range Behavior: If the input is outside the range of -1 to 1, the function returns NaN (Not a Number) or null.
  • Null Handling: If the input expression is null, the function returns null.
  • Radians to Degrees: The result is provided in radians. To convert the result to degrees, multiply the return value by 180 / PI().
  • Common Use Cases: This function is typically used within the alter stage for geometric calculations, spatial analysis, or normalizing data vectors.

Examples

Example 1: Calculate arcsine of literal values

Goal: Calculate the inverse sine for specific numerical literals to see the radian results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = asin(0), result2 = asin(1), result3 = asin(-1)
| fields result1, result2, result3

Explanation: You use asin() on three different literal values. asin(0) returns 0.0, asin(1) returns approximately 1.5708 (π/2), and asin(-1) returns approximately -1.5708 (-π/2).

Output:

RESULT1 RESULT2 RESULT3
0.0 1.5708 -1.5708

Example 2: Calculate arcsine from a field value

Goal: Calculate the arcsine of values stored in a specific field, ensuring they are within the valid mathematical range.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter duration_seconds >= -1 and duration_seconds <= 1
| alter arc_sin_val = asin(duration_seconds)
| fields event_id, duration_seconds, arc_sin_val
| limit 3

Explanation: This query first filters the dataset to ensure duration_seconds contains only values between -1 and 1, then computes the arcsine for each and stores it in arc_sin_val.

Output:

EVENT_ID DURATION_SECONDS ARC_SIN_VAL
101 0.5 0.5236
102 -0.3 -0.3047
103 1.0 1.5708

avg (comp)

Use the avg() function to calculate the average value of a numerical field within a group of rows. When used with the comp stage, it computes a single statistical average for the specified field across all records sharing matching values defined in a grouping clause.

Syntax

comp avg(<field>) [as <alias>] [by <field1>[,<field2>...]] [addrawdata = true|false [as <target field>]]

Parameters

Name Type Required Description
field integer, float Yes The numerical field for which to calculate the average.
alias string No An optional name for the resulting average field.
field1, field2... any No Optional fields used to group rows for independent average calculations.
addrawdata boolean No If true, includes a column listing the raw events contributing to the aggregate. Defaults to false.
target field string No An optional alias for the raw data column if addrawdata is true.

Returns

The avg() function returns a single numerical value representing the average of the input field for the specified group.

Usage Notes

  • The avg() function operates exclusively on numerical fields, including both integers and floating-point numbers.
  • The comp stage must always precede the avg() function.
  • When the comp stage is used, the system field _time is removed from the result set unless explicitly included in a by clause.
  • Calculated columns created by the comp stage are typically appended as the last columns in the result set.
  • If addrawdata is set to true, the query supports up to 50 defined fields and displays up to 100 raw events.

Examples

Example 1: Average a field across the entire dataset

Goal: Calculate the average value of the duration_seconds field for all records in the dataset without grouping.

XQL Code:

config timeframe = 1d  
| dataset = sample_xql_raw  
| comp avg(duration_seconds) as overall_avg_duration

Explanation: The avg() function computes the mean of all duration_seconds values present in the sample_xql_raw dataset and names the result overall_avg_duration.

Output:

overall_avg_duration
10.285

Example 2: Average a field grouped by another field

Goal: Calculate the average duration_seconds separately for successful and unsuccessful events.

XQL Code:

config timeframe = 1d  
| dataset = sample_xql_raw  
| comp avg(duration_seconds) as avg_duration_by_status by is_successful

Explanation: This code groups records by their is_successful status and then calculates the average duration_seconds for each distinct group.

Output:

is_successful avg_duration_by_status
true 14.271428571428571
false 0.9833333333333333

Example 3: Average with raw data inclusion

Goal: Calculate average duration and include the JSON representation of raw events that contributed to each group.

XQL Code:

config timeframe = 1d  
| dataset = sample_xql_raw  
| fields is_successful, duration_seconds  
| comp avg(duration_seconds) by is_successful addrawdata = true as raw_events_for_avg

Explanation: This code calculates the average duration_seconds grouped by is_successful and uses the addrawdata = true option to generate a column containing the underlying raw data for each aggregate.

Output:

is_successful avg_duration_by_status raw_events_for_avg
true 14.271428571428571 [{"is_successful": true, "duration_seconds": 1.5}, ...]
false 0.9833333333333333 [{"is_successful": false, "duration_seconds": 0.8}, ...]

avg (windowcomp)

Use the avg() function within the windowcomp stage to calculate and return a single average value of a specified numerical field over a defined "window" or group of rows. The resulting average value is then presented for each row within that window frame, allowing for contextual statistical analysis without collapsing rows.

Syntax

windowcomp avg(<field>) [by <field> [,<field>,...]] [sort [asc|desc] <field1> [, [asc|desc] <field2>,...]] [between 0|null|<number>|-<number> [and 0|null|<number>|-<number>] [frame_type=range]] [as <alias>] 

Parameters

Name Type Required Description
field numeric Yes The numerical field for which the average will be calculated.
by field string No Partitions the data into distinct groups, allowing the avg() function to operate independently within each of these partitions.
sort field string No Defines the order of rows within each partition. This ordering is critical when defining a window using the between clause.
between clause string No Defines the "window frame"—a specific range of rows relative to the current row—over which the avg() function calculates.
alias string No Assigns an alias name to the new column displaying the result.

Returns

The avg() function returns a single numerical value representing the average for the defined window. This value is displayed in a new column for every row in the group of rows it applies to.

Usage Notes

  • The windowcomp stage must always precede analytic functions like avg() that calculate statistics.
  • Only one function can be defined per field within a single windowcomp stage.
  • If the between clause is omitted, the default window typically encompasses the entire partition.

Examples

Example 1: avg() over an entire partition (default window)

Goal: Calculate the average of duration_seconds for all events within each is_successful group (partition), and replicate this single average value for every row in that group.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| fields event_id, _time, is_successful, duration_seconds   
| windowcomp avg(duration_seconds) by is_successful as avg_in_partition   
| sort asc is_successful, asc _time, asc event_id   
| limit 10

Explanation: The by is_successful clause partitions the data into two groups: one for false and one for true. The avg() function then calculates the average of the duration_seconds field for each partition. The result is a single average value for each partition, which is then displayed for every row in that partition. The sort clause ensures the rows are ordered by is_successful,_time, and event_id for consistent presentation of the data.

Output:

_time event_id is_successful duration_seconds avg_in_partition
2023-10-26 10:05:30 UTC 102 false 0.8 0.9833
2023-10-26 10:40:10 UTC 106 false 2.1 0.9833
2023-10-26 10:55:55 UTC 109 false 0.05 0.9833
2023-10-26 10:00:00 UTC 101 true 1.5 14.2714
2023-10-26 10:15:15 UTC 103 true 10.2 14.2714
2023-10-26 10:20:00 UTC 104 true 0.1 14.2714
2023-10-26 10:30:45 UTC 105 true 5.0 14.2714
2023-10-26 10:45:00 UTC 107 true 7.8 14.2714
2023-10-26 10:50:20 UTC 108 true 15.3 14.2714
2023-10-26 11:00:10 UTC 110 true 60.0 14.2714

Example 2: avg() with a rolling window (preceding 2 rows and current row)**

Goal: Calculate a rolling average from the duration_seconds of the current row and the two immediately preceding rows within its partition, ordered by _time.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| fields event_id, _time, is_successful, duration_seconds   
| windowcomp avg(duration_seconds) by is_successful sort asc _time between -2 and 0 as rolling_avg   
| sort asc is_successful, asc _time, asc event_id   
| limit 10

Explanation: The code defines a rolling window using between -2 and 0 which includes the current row (0) and the two immediately preceding rows (-2, -1) within its partition. For each row, the avg() function calculates the average of duration_seconds strictly within this rolling window. If there are not enough preceding rows, the window starts from the beginning of the partition.

Output:

_time event_id is_successful duration_seconds rolling_avg
2023-10-26 10:05:30 UTC 102 false 0.8 0.8
2023-10-26 10:40:10 UTC 106 false 2.1 1.45
2023-10-26 10:55:55 UTC 109 false 0.05 0.9833
2023-10-26 10:00:00 UTC 101 true 1.5 1.5
2023-10-26 10:15:15 UTC 103 true 10.2 5.85
2023-10-26 10:20:00 UTC 104 true 0.1 3.9333
2023-10-26 10:30:45 UTC 105 true 5.0 5.1
2023-10-26 10:45:00 UTC 107 true 7.8 4.3
2023-10-26 10:50:20 UTC 108 true 15.3 9.3667
2023-10-26 11:00:10 UTC 110 true 60.0 27.7

Example 3: avg() with a fixed window (current row to unbounded following row)

Goal: Calculate the average for each row over a window that starts from the current row and extends to the end of its partition.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| fields event_id, _time, is_successful, duration_seconds   
| windowcomp avg(duration_seconds) by is_successful sort asc _time between 0 and null as avg_from_current   
| sort asc is_successful, asc _time, asc event_id   
| limit 10

Explanation: The code uses the between 0 and null clause to set a window starting from the current row (0) through to the very end of the partition (null). For each row, the avg() calculates the average of duration_seconds utilizing the current row's value up to the last value in its is_successful partition.

Output:

_time event_id is_successful duration_seconds avg_from_current
2023-10-26 10:05:30 UTC 102 false 0.8 0.9833
2023-10-26 10:40:10 UTC 106 false 2.1 1.075
2023-10-26 10:55:55 UTC 109 false 0.05 0.05
2023-10-26 10:00:00 UTC 101 true 1.5 14.2714
2023-10-26 10:15:15 UTC 103 true 10.2 16.4
2023-10-26 10:20:00 UTC 104 true 0.1 17.64
2023-10-26 10:30:45 UTC 105 true 5.0 22.025
2023-10-26 10:45:00 UTC 107 true 7.8 27.7
2023-10-26 10:50:20 UTC 108 true 15.3 37.65
2023-10-26 11:00:10 UTC 110 true 60.0 60.0

bitwise_and

Use the bitwise_and() function to perform a bitwise AND operation between two integer values.

Syntax

bitwise_and (<left_clause>, <right_clause>)

Parameters

Name Type Required Description
left_clause integer Yes The first integer value or field on which to perform the bitwise AND.
right_clause integer Yes The second integer value or field to AND against the first value.

Returns

The bitwise_and() function returns an integer representing the result of the bitwise AND operation between the two input parameters.

Usage notes

  • The function performs a bitwise AND (&) operation, comparing each bit of the first operand to the corresponding bit of the second operand. If both bits are 1, the corresponding result bit is set to 1; otherwise, it is set to 0.
  • Both parameters must be integers. Passing a string value will result in a validation error.
  • This function is commonly used to check whether specific bit flags are set in a bitmask field.
  • The function supports hexadecimal integer values when used with to_integer() (for example, to_integer("0x02")).
  • The function is typically used within the alter or filter stages to create computed fields or filter events based on bitwise conditions.

Examples

Example 1: Check a specific bit flag in an integer field

Goal: Use a bit mask to check whether a specific flag (bit 1, value 2) is set in the xdm.case.score field.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter flag_check = bitwise_and(xdm.case.score, 2) 
| fields event_id, xdm.case.score, flag_check 
| limit 3

Explanation: This query performs a bitwise AND between xdm.case.score and 2 (binary 10). If bit 1 is set in xdm.case.score, flag_check will be 2; otherwise, it will be 0.

Output:

event_id xdm.case.score flag_check
101 7 2
102 4 0
103 3 2

Example 2: Filter events using a bitwise AND with hexadecimal values

Goal: Filter events where a specific bit flag (0x02) is set in the xdm.case.score field, using hexadecimal notation.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| filter bitwise_and(xdm.case.score, to_integer("0x02")) > 0
| fields event_id, xdm.case.score 
| limit 3

Explanation: This query filters for events where bit 1 (value 2) is set in xdm.case.score. The to_integer("0x02") converts the hexadecimal value 0x02 to the integer 2, which is used as the bitmask. The bitwise_and() result is either 2 (bit is set) or 0 (bit is not set). Only events where the result is greater than 0 pass the filter.

Output:

event_id xdm.case.score
103 7
107 15
112 6

Example 3: Mask out the lower 8 bits of an integer field

Goal: Extract the lower 8 bits from a numeric field using a bit mask of 255 (binary 11111111).

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter lower_byte = bitwise_and(action_status, 255) 
| fields event_id, action_status, lower_byte 
| limit 3

Explanation: This query isolates the lower 8 bits of the action_status field. For a value of 258 (binary 100000010), the result is 2 (binary 00000010).

Output:

event_id action_status lower_byte
101 258 2
102 511 255
103 1024 0

bitwise_or

Use the bitwise_or() function to perform a bitwise OR operation between two integer values.

Syntax

bitwise_or (<left_clause>, <right_clause>)

Parameters

Name Type Required Description
left_clause integer Yes The first integer value or field on which to perform the bitwise OR.
right_clause integer Yes The second integer value or field to OR against the first value.

Returns

The bitwise_or() function returns an integer representing the result of the bitwise OR operation between the two input parameters.

Usage notes

  • The function performs a bitwise OR (|) operation, comparing each bit of the first operand to the corresponding bit of the second operand. If either bit is 1, the corresponding result bit is set to 1; otherwise, it is set to 0.
  • Both parameters must be integers. Passing a string value will result in a validation error.
  • This function is commonly used to set specific bit flags or combine multiple bitmask values into a single field.
  • The function supports hexadecimal integer values when used with to_integer() (for example, to_integer("0x000b")).
  • The function is typically used within the alter or filter stages to create computed fields or filter events based on bitwise conditions.

Examples

Example 1: Set a specific bit flag in an integer field

Goal: Set bit 2 (value 4) in the event_type field, ensuring that flag is always present in the result.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter with_flag = bitwise_or(event_type, 4) 
| fields event_id, event_type, with_flag 
| limit 3

Explanation: This query performs a bitwise OR between event_type and 4 (binary 100). If event_type is 3 (binary 011), the result is 7 (binary 111), ensuring bit 2 is set.

Output:

event_id event_type with_flag
101 3 7
102 5 5
103 0 4

Example 2: Combine two hexadecimal values and filter

Goal: Combine two hexadecimal values using bitwise OR and filter events where the result exceeds a threshold.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| filter bitwise_or(to_integer("0x0004"), to_integer("0x000b")) > 2 
| fields event_id, event_type 
| limit 3

Explanation: This query computes the bitwise OR of 0x0004 (decimal 4, binary 0100) and 0x000b (decimal 11, binary 1011), resulting in 15 (binary 1111). Since 15 > 2, all events pass the filter.

Output:

event_id event_type
101 5
102 3
103 7

Example 3: Merge flags from two separate fields

Goal: Merge the bit flags from two separate integer fields into a single combined field.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter combined_flags = bitwise_or(permission_read, permission_write) 
| fields event_id, permission_read, permission_write, combined_flags 
| limit 3

Explanation: This query combines the bit flags from permission_read and permission_write. For example, if permission_read is 1 (binary 01) and permission_write is 2 (binary 10), the result is 3 (binary 11), indicating both permissions are active.

Output:

event_id permission_read permission_write combined_flags
101 1 2 3
102 4 1 5
103 0 8 8

bitwise_sleft

Use the bitwise_sleft() function to perform a bitwise left shift operation on an integer value by a specified number of positions.

Syntax

bitwise_sleft (<left_clause>, <right_clause>)

Parameters

Name Type Required Description
left_clause integer Yes The integer value or field whose bits are to be shifted left.
right_clause integer Yes The number of bit positions to shift left.

Returns

The bitwise_sleft() function returns an integer representing the result of shifting the bits of the first parameter to the left by the number of positions specified in the second parameter.

Usage notes

  • The function performs a bitwise left shift (<<) operation, moving each bit of the value to the left by the specified number of positions. New bits on the right are filled with 0.
  • Each left shift by 1 position effectively multiplies the value by 2. Shifting left by n positions multiplies the value by 2^n.
  • Both parameters must be integers. Passing a string value will result in a validation error.
  • This function is useful for constructing bitmask values, scaling values by powers of two, or encoding data into specific bit positions.
  • The function is typically used within the alter or filter stages to create computed fields or filter events based on bitwise conditions.

Examples

Example 1: Shift a field value left by 1 position

Goal: Double the value of the event_type field by shifting its bits one position to the left.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter doubled_type = bitwise_sleft(event_type, 1) 
| fields event_id, event_type, doubled_type 
| limit 3

Explanation: This query shifts the bits of event_type one position to the left, effectively multiplying by 2. For a value of 5 (binary 101), the result is 10 (binary 1010).

Output:

event_id event_type doubled_type
101 5 10
102 3 6
103 7 14

Example 2: Create a bitmask from a bit position

Goal: Convert a bit position number into its corresponding bitmask value by shifting 1 to the left.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter bitmask = bitwise_sleft(1, bit_position) 
| fields event_id, bit_position, bitmask 
| limit 3

Explanation: This query creates a bitmask by shifting the value 1 left by the number of positions specified in bit_position. For position 3, the result is 8 (binary 1000). For position 0, the result is 1 (binary 1).

Output:

event_id bit_position bitmask
101 0 1
102 3 8
103 7 128

Example 3: Scale a value by a power of two and filter

Goal: Multiply the action_status field by 256 (2^8) by shifting left 8 positions, then filter for results above a threshold.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter upper_byte = bitwise_sleft(action_status, 8) 
| filter upper_byte > 512 
| fields event_id, action_status, upper_byte 
| limit 3

Explanation: This query shifts action_status left by 8 positions, multiplying it by 256. For a value of 3, the result is 768. Only results greater than 512 pass the filter.

Output:

event_id action_status upper_byte
102 3 768
103 10 2560
105 5 1280

bitwise_sright

Use the bitwise_sright() function to perform a bitwise right shift operation on an integer value by a specified number of positions.

Syntax

bitwise_sright (<left_clause>, <right_clause>)

Parameters

Name Type Required Description
left_clause integer Yes The integer value or field whose bits are to be shifted right.
right_clause integer Yes The number of bit positions to shift right.

Returns

The bitwise_sright() function returns an integer representing the result of shifting the bits of the first parameter to the right by the number of positions specified in the second parameter.

Usage notes

  • The function performs a bitwise right shift (>>) operation, moving each bit of the value to the right by the specified number of positions. Bits shifted beyond the least significant position are discarded.
  • Each right shift by 1 position effectively performs integer division by 2. Shifting right by n positions divides the value by 2^n (discarding any remainder).
  • Both parameters must be integers. Passing a string value will result in a validation error.
  • This function is useful for extracting values from specific bit positions within a packed integer, or for dividing by powers of two.
  • The function is typically used within the alter or filter stages to create computed fields or filter events based on bitwise conditions.

Examples

Example 1: Shift a field value right by 1 position

Goal: Halve the value of the event_type field by shifting its bits one position to the right.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter halved_type = bitwise_sright(event_type, 1) 
| fields event_id, event_type, halved_type 
| limit 3

Explanation: This query shifts the bits of event_type one position to the right, effectively performing integer division by 2. For a value of 7 (binary 111), the result is 3 (binary 11).

Output:

event_id event_type halved_type
101 7 3
102 10 5
103 4 2

Example 2: Extract the upper byte from a 16-bit value

Goal: Extract the upper 8 bits from a 16-bit integer field by shifting right 8 positions.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter upper_byte = bitwise_sright(action_status, 8) 
| fields event_id, action_status, upper_byte 
| limit 3

Explanation: This query shifts action_status right by 8 positions, extracting the upper byte. For a value of 512 (binary 0000001000000000), the result is 2. For 1024, the result is 4.

Output:

event_id action_status upper_byte
101 512 2
102 1024 4
103 256 1

Example 3: Check a specific bit by shifting and masking

Goal: Determine whether bit 4 is set in the event_type field by shifting right 4 positions and checking the least significant bit with bitwise_and().

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter bit4_value = bitwise_and(bitwise_sright(event_type, 4), 1) 
| fields event_id, event_type, bit4_value 
| limit 3

Explanation: This query first shifts event_type right by 4 positions, then uses bitwise_and() with 1 to isolate the least significant bit. If bit 4 was set in the original value, the result is 1; otherwise, it is 0. For a value of 16 (binary 10000), the result is 1.

Output:

event_id event_type bit4_value
101 16 1
102 7 0
103 31 1

bitwise_xor

Use the bitwise_xor() function to perform a bitwise exclusive OR (XOR) operation between two integer values.

Syntax

bitwise_xor (<left_clause>, <right_clause>)

Parameters

Name Type Required Description
left_clause integer Yes The first integer value or field on which to perform the bitwise XOR.
right_clause integer Yes The second integer value or field to XOR against the first value.

Returns

The bitwise_xor() function returns an integer representing the result of the bitwise exclusive OR operation between the two input parameters.

Usage notes

  • The function performs a bitwise XOR (^) operation, comparing each bit of the first operand to the corresponding bit of the second operand. If the bits are different, the corresponding result bit is set to 1; if the bits are the same, it is set to 0.
  • Both parameters must be integers. Passing a string value will result in a validation error.
  • This function is commonly used to toggle specific bit flags, detect differences between two bitmask fields, or perform simple obfuscation.
  • The XOR operation is its own inverse: applying bitwise_xor() twice with the same value returns the original value (for example, bitwise_xor(bitwise_xor(a, b), b) equals a).
  • The function is typically used within the alter or filter stages to create computed fields or filter events based on bitwise conditions.

Examples

Example 1: Toggle a specific bit flag in an integer field

Goal: Toggle bit 2 (value 4) in the event_type field. If the bit is set, it will be cleared; if it is cleared, it will be set.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter toggled_type = bitwise_xor(event_type, 4) 
| fields event_id, event_type, toggled_type 
| limit 3

Explanation: This query performs a bitwise XOR between event_type and 4 (binary 100). For a value of 7 (binary 111), bit 2 is cleared, resulting in 3 (binary 011). For a value of 3 (binary 011), bit 2 is set, resulting in 7 (binary 111).

Output:

event_id event_type toggled_type
101 7 3
102 3 7
103 5 1

Example 2: Detect differences between two bitmask fields

Goal: Identify which bits differ between two integer fields by computing their XOR.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter diff_bits = bitwise_xor(permission_before, permission_after) 
| fields event_id, permission_before, permission_after, diff_bits 
| limit 3

Explanation: This query computes the XOR of permission_before and permission_after. The result contains 1-bits only in positions where the two fields differ. For values 7 (binary 111) and 5 (binary 101), the result is 2 (binary 010), indicating bit 1 changed.

Output:

event_id permission_before permission_after diff_bits
101 7 5 2
102 3 3 0
103 12 9 5

Example 3: Filter events where specific bits changed

Goal: Filter for events where at least one of the lower 4 bits changed between two status fields.

XQL code:

config timeframe = 1d 
| dataset = xdr_data 
| alter changed_bits = bitwise_and(bitwise_xor(status_before, status_after), 15) 
| filter changed_bits > 0 
| fields event_id, status_before, status_after, changed_bits 
| limit 3

Explanation: This query first computes the XOR of status_before and status_after to find all differing bits, then uses bitwise_and() with 15 (binary 1111) to isolate the lower 4 bits. Only events where at least one of the lower 4 bits changed pass the filter.

Output:

event_id status_before status_after changed_bits
101 7 5 2
103 12 9 5
106 15 0 15

cbrt

Use the cbrt() function to calculate the cube root of a numeric value.

Syntax

cbrt(<number>)

Parameters

Name Type Required Description
number integer, float Yes The numeric value for which to calculate the cube root. Any real number is valid, including negative numbers.

Returns

Type: float

Description: The cbrt() function returns the cube root of the input value. If the input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The cbrt() function accepts any real number, including negative numbers and zero.
  • Null Handling: If the input expression is null, the function returns null.
  • Negative Numbers: Unlike square root (sqrt()), the cube root of a negative number is a valid real number. For example, cbrt(-8) returns -2.0.
  • Identity Values: cbrt(0) returns 0.0, cbrt(1) returns 1.0, and cbrt(-1) returns -1.0.
  • Common Use Cases: This function is typically used within the alter stage for mathematical transformations, volume calculations, and data normalization.

Examples

Example 1: Calculate cube root of literal values

Goal: Calculate the cube root for specific numeric literals to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = cbrt(8), result2 = cbrt(27), result3 = cbrt(-8)
| fields result1, result2, result3

Explanation: You use cbrt() on three different literal values. cbrt(8) returns 2.0, cbrt(27) returns 3.0, and cbrt(-8) returns -2.0, demonstrating that the function handles negative inputs correctly.

Output:

RESULT1 RESULT2 RESULT3
2.0 3.0 -2.0

Example 2: Calculate cube root from a field value

Goal: Calculate the cube root of values stored in a dataset field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter cube_root_val = cbrt(numeric_value)
| fields event_id, numeric_value, cube_root_val
| limit 3

Explanation: This query computes the cube root for each value in the numeric_value field using cbrt() in the alter stage and stores the result in cube_root_val. No filtering is needed since cbrt() accepts any real number.

Output:

EVENT_ID NUMERIC_VALUE CUBE_ROOT_VAL
101 125.0 5.0
102 -27.0 -3.0
103 1000.0 10.0

Example 3: Combine cube root with other operations

Goal: Use the cube root function as part of a larger mathematical expression.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter volume = multiply(numeric_value, multiply(numeric_value, numeric_value))
| alter side_length = cbrt(volume)
| fields event_id, numeric_value, volume, side_length
| limit 3

Explanation: This query first cubes the numeric_value to simulate a volume, then applies cbrt() to recover the original side length. The result in side_length should match the original numeric_value, demonstrating that cbrt() is the inverse of cubing.

Output:

EVENT_ID NUMERIC_VALUE VOLUME SIDE_LENGTH
101 3.0 27.0 3.0
102 5.0 125.0 5.0
103 10.0 1000.0 10.0

ceil

Use the ceil() function to convert a field containing a number and return an integer rounded up to the nearest whole number.

Syntax

ceil (<number>)

Parameters

Name Type Required Description
number integer, float Yes The numeric value to round up.

Returns

The ceil() function returns an integer representing the input number rounded up to the nearest whole number.

Usage notes

  • The function accepts a field that contains a number, meaning it can take integers or floating-point numbers as input.
  • The function explicitly rounds the number up to the nearest whole integer.
  • For positive numbers, this behavior rounds up past the decimal part (for example, 1.1 becomes 2).
  • For negative numbers, it rounds toward zero (for example, -2.5 becomes -2).

Examples

Example 1: Round up positive floating-point field

Goal: Round the duration_seconds field up to the nearest integer.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter ceiled_duration = ceil(duration_seconds) 
| fields event_id, duration_seconds, ceiled_duration 
| limit 3 

Explanation: This query creates a new field, ceiled_duration, by taking the duration_seconds (for example, 1.5, 0.8) and rounding it up. For 1.5, it becomes 2; for 0.8, it becomes 1.

Output:

EVENT_ID DURATION_SECONDS CEILED_DURATION
101 1.5 2
102 0.8 1
103 10.2 11

Example 2: Round up calculated floating-point value

Goal: Round the result of a division operation on an integer field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter divided_event_id = divide(event_id, 3) 
| alter ceiled_divided_id = ceil(divided_event_id) 
| fields event_id, divided_event_id, ceiled_divided_id 
| limit 3 

Explanation: Here, event_id (for example, 101) is divided by 3, resulting in 33.666.... The ceil() function then rounds this up to 34.

Output:

EVENT_ID DIVIDED_EVENT_ID CEILED_DIVIDED_ID
101 33.666666 34
102 34.0 34
103 34.333333 35

Example 3: Round up negative number

Goal: Round a negative floating-point number to demonstrate rounding towards zero.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter negative_value = subtract(0, duration_seconds) 
| alter ceiled_negative = ceil(negative_value) 
| fields event_id, duration_seconds, negative_value, ceiled_negative 
| limit 3 

Explanation: This query negates the duration_seconds field and then applies ceil(). For a negative value like -1.5, ceil() rounds toward zero, resulting in -1. For -0.8, it becomes 0.

Output:

EVENT_ID DURATION_SECONDS NEGATIVE_VALUE CEILED_NEGATIVE
101 1.5 -1.5 -1
102 0.8 -0.8 0
103 10.2 -10.2 -10

Example 4: Round up number extracted from JSON

Goal: Round a numeric value extracted from a JSON field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 110 
| alter json_size_string = simple_json_data -> size_gb 
| alter json_size_number = to_number(json_size_string) 
| alter ceiled_json_size = ceil(json_size_number) 
| fields event_id, simple_json_data, json_size_number, ceiled_json_size 
| limit 1 

Explanation: This query extracts the size_gb value from the simple_json_data field (for example, "500" for event_id 110). The query then converts this string to a number and applies ceil(), which for an integer like 500 would result in 500.

Output:

EVENT_ID SIMPLE_JSON_DATA JSON_SIZE_NUMBER CEILED_JSON_SIZE
110 {"backup_id": "DB-005", "size_gb": 500} 500 500

coalesce

Use the coalesce() function to return the first non-NULL value from a defined list of input fields or expressions.

Syntax

coalesce (<field_1>, <field_2>, ...<field_n>)

Parameters

Name Type Required Description
field_n Any Yes An arbitrary number of fields or expressions. The function evaluates them from left to right.

Returns

The coalesce() function returns a single value whose data type matches the first non-NULL argument it encounters. If all arguments are NULL, the function returns NULL.

Usage notes

  • Arguments are evaluated strictly from left to right. When a non-NULL value is found, the evaluation stops, and that value is returned.
  • This function is valuable for ensuring data completeness and providing fall-back mechanisms, allowing you to define a prioritized list of data sources for a single field.

Examples

Example 1: Coalescing literal values of different types

Goal: Demonstrate the core "first non-NULL" behavior by providing various literal values, including NULLs, of different data types.

XQL code:

config timeframe = 1d // Sets the query timeframe 
| dataset = sample_xql_raw // Specifies the dataset to use 
| alter chosen_value_str = coalesce(NULL, "Fallback String", "Another Option") // First non-NULL string 
| alter chosen_value_int = coalesce(NULL, 123, NULL, 456) // First non-NULL integer 
| alter chosen_value_bool = coalesce(false, NULL, true) // First non-NULL boolean 
| fields event_id, chosen_value_str, chosen_value_int, chosen_value_bool 
| limit 3 

Explanation: For chosen_value_str, coalesce() skips NULL and returns "Fallback String". For chosen_value_int, it returns 123. For chosen_value_bool, it returns false. This demonstrates the function's ability to handle various data types and select the first available non-NULL literal.

Output:

EVENT_ID CHOSEN_VALUE_STR CHOSEN_VALUE_INT CHOSEN_VALUE_BOOL
101 Fallback String 123 false
102 Fallback String 123 false
103 Fallback String 123 false

Example 2: Coalescing existing fields

Goal: Use coalesce() with existing fields, specifically leveraging a field that can be NULL (dst_domain) and one that is always present (event_description) as a fallback.

XQL code:

config timeframe = 1d // Sets the query timeframe 
| dataset = sample_xql_raw // Specifies the dataset to use 
// dst_domain is NULL for event_id 105, event_description is always present 
| alter primary_or_fallback_description = coalesce(dst_domain, event_description) 
| fields event_id, dst_domain, event_description, primary_or_fallback_description 
| limit 5 

Explanation: For events where dst_domain is not NULL (for example, event 101-104), coalesce() returns the dst_domain. For event 105, where dst_domain is NULL, coalesce() falls back to event_description, providing "Data transformation".

Output:

EVENT_ID DST_DOMAIN EVENT_DESCRIPTION PRIMARY_OR_FALLBACK_DESCRIPTION
101 ec2.amazonaws.com User login successful ec2.amazonaws.com
102 sts.amazonaws.com File access attempt sts.amazonaws.com
103 www.google.com Network connection established www.google.com
104 dropbox.com System heartbeat dropbox.com
105 NULL Data transformation Data transformation

Example 3: Coalescing multiple JSON paths for a single concept

Goal: Handle variations in JSON data where different keys might represent the same logical piece of information (for example, status code under code or error).

XQL code:

config timeframe = 1d // Sets the query timeframe 
| dataset = sample_xql_raw // Specifies the dataset to use 
// Extract 'code' or 'error' from JSON, using the first available 
| alter status_code = coalesce(simple_json_data -> code, simple_json_data -> error) 
| fields event_id, simple_json_data, status_code 
| limit 3 

Explanation: For event 101, code (200) is found first. For event 102, code is not present, so error ("access_denied") is returned. For event 103, neither code nor error_code exists, resulting in NULL.

Output:

EVENT_ID SIMPLE_JSON_DATA STATUS_CODE
101 {"status": "ok", "code": 200} 200
102 {"status": "fail", "error": "access_denied"} access_denied
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL

Example 4: Coalescing complex nested JSON paths for a unified identifier

Goal: Extract a "principal name" from multiple possible nested JSON paths within the nested_json_data field, such as user.name, process.name, client.id, or db.name.

XQL code:

config timeframe = 1d // Sets the query timeframe 
| dataset = sample_xql_raw // Specifies the dataset to use
| filter event_id in(101, 102, 108, 109, 110)
// Attempt to find a 'principal_name' from various nested JSON paths 
| alter principal_name = coalesce( 
    nested_json_data -> user.name, 
    nested_json_data -> process.name, 
    nested_json_data -> client.id, 
    nested_json_data -> db.name 
) 
| fields event_id, nested_json_data, principal_name 
| limit 5 

Explanation: For event 101, user.name ("Alice") is found first. For event 102, user.name is missing, but process.name ("cmd.exe") is present. For event 108, none of the specified paths exist, resulting in NULL. For event 109, client.id ("C2") is found. For event 110, db.name ("prod_db") is found.

Output:

EVENT_ID NESTED_JSON_DATA PRINCIPAL_NAME
101 {"user": {"id": "U1", "name": "Alice"}, "session": {"start": "10:00", "type": "web"}} Alice
102 {"process": {"name": "cmd.exe", "pid": 1234}, "target": {"path": "/var/log", "permission": "rwx"}} cmd.exe
108 {"system":{"hostname":"webserver01","os":"Linux"},"patch":{"version":"1.2.3"}} NULL
109 {"client":{"id":"C2","api_key":"xyz"},"request":{"endpoint":"/data","rate":100}} C2
110 {"db":{"name":"prod_db","type":"SQL"},"storage":{"location":"S3","cost_usd":15}} prod_db

Example 5: Selecting the first non-null username from multiple fields

Goal: Evaluate three different username fields (actor_primary_username, os_actor_primary_username, and causality_actor_primary_username) and return the first available (non-null) value to populate a single username column.

XQL code:

|dataset = sample_xql_raw
| fields actor_primary_username,
       os_actor_primary_username,
       causality_actor_primary_username 
| alter username = coalesce(actor_primary_username,
                          os_actor_primary_username,
                          causality_actor_primary_username) 

Explanation: The coalesce() function checks the provided arguments in order from left to right. The function returns the value of the first field that is not null. If actor_primary_username is null, it checks os_actor_primary_username, and so on. This is a common technique for normalizing data when the same information might be stored in different fields depending on the event source.

Output:

actor_primary_username os_actor_primary_username causality_actor_primary_username username
null "admin_user" "system" "admin_user"
"jsmith" "jsmith_os" null "jsmith"
null null "root" "root"
null null null null

concat

Use the concat() function to join two or more strings into a single, cohesive string.

Syntax

concat (<string1>, <string2>, ...)

Parameters

Name Type Required Description
string1, string2, ... string Yes The string expressions whose values will be joined. Two or more strings are required.

Returns

The concat() function returns a single string.

Usage notes

  • The function strictly accepts string parameters.
  • The concat() function will not perform any implicit conversion of other data types to strings.
  • Explicit to_string() conversion is necessary for non-string values (such as integers, floats, or booleans) to ensure type compatibility.
  • If any of the values passed to concat() are NULL, the function will return NULL.

Examples

Example 1: Concatenating two string literal values

Goal: Join two static string values into a new field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter static_message = concat("Investigation: ", "Started") 
| fields event_id, static_message 
| limit 3

Explanation: For each record, the concat() function combines the two literal strings "Investigation: " and "Started", resulting in the value "Investigation: Started" for all records.

Output:

EVENT_ID STATIC_MESSAGE
101 "Investigation: Started"
102 "Investigation: Started"
103 "Investigation: Started"

Example 2: Concatenating a string literal with a field value

Goal: Join a fixed string prefix with the value of an existing field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_label = concat("Event ID: ", to_string(event_id)) 
| fields event_id, event_label 
| limit 3

Explanation: The to_string(event_id) function converts the numeric event_id into its string representation. concat() then joins the literal string "Event ID: " with the string version of the event_id, creating a unique event_label for each record.

Output:

EVENT_ID EVENT_LABEL
101 "Event ID: 101"
102 "Event ID: 102"
103 "Event ID: 103"

Example 3: Concatenating multiple field values

Goal: Combine values from multiple existing fields into a single string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_summary = concat(event_description, " (Success: ", to_string(is_successful), ", Duration: ", to_string(duration_seconds), ")") 
| fields event_id, event_description, is_successful, duration_seconds, event_summary 
| limit 3

Explanation: The functions to_string(is_successful) and to_string(duration_seconds) convert the boolean and numeric fields into strings. concat() then combines event_description, literal strings like " (Success: ", and the converted string representations into event_summary.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL DURATION_SECONDS EVENT_SUMMARY
101 "User login successful" true 1.5 "User login successful (Success: true, Duration: 1.5)"
102 "File access attempt" false 0.8 "File access attempt (Success: false, Duration: 0.8)"
103 "Network connection established" true 10.2 "Network connection established (Success: true, Duration: 10.2)"

Example 4: Concatenating extracted JSON scalar values

Goal: Use concat() with values extracted from a JSON field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter status_code_value = coalesce(simple_json_data -> code, simple_json_data -> error_code) 
| alter full_status_message = concat("Status: ", status_code_value) 
| fields event_id, simple_json_data, status_code_value, full_status_message 
| limit 3

Explanation: The coalesce function attempts to get either code or error_code from simple_json_data as a string. concat() combines "Status: " with the extracted status_code_value. Because concat() returns NULL if any input is NULL, full_status_message is NULL for event 103 where neither key exists.

Output:

EVENT_ID SIMPLE_JSON_DATA STATUS_CODE_VALUE FULL_STATUS_MESSAGE
101 {"status": "ok", "code": 200} "200" "Status: 200"
102 {"status": "fail", "error": "access_denied"} "access_denied" "Status: access_denied"
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL NULL

Example 5: Concatenating converted boot time values

Goal: Use concat() to prepend a string prefix to a converted timestamp field.

XQL code:

dataset = xdr_data 
| fields action_boot_time as abt 
| filter abt != null 
| alter abt_string = concat("str: ", to_string(abt)) 
| limit 1

Explanation: The query filters the xdr_data dataset to find the first record where action_boot_time is not NULL. Since action_boot_time is typically a numeric or timestamp type, the to_string() function is used to convert it before the concat() function joins it with the literal prefix "str: ".

Output:

ABT ABT_STRING
1675238400 "str: 1675238400"

convert_from_base_64

Use the convert_from_base_64() function to decode a base64-encoded input string and return it in its original, decoded string format.

Syntax

convert_from_base_64 ("<base64-encoded input>")

Parameters

Name Type Required Description
encoded_string string Yes The base64-encoded string to decode.

Returns

The convert_from_base_64() function returns the decoded value as a string.

Usage notes

  • The function requires a single string input that is base64-encoded.
  • The function consistently returns the decoded value as a string.
  • The function is typically employed within the alter stage to create new fields or modify existing ones by decoding string data.
  • The function can also be used within a filter stage if the decoding result is needed for a conditional check.

Examples

Example 1: Decoding a basic literal base64 string

Goal: Decode a common base64 string that represents "Hello world".

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter decoded_string = convert_from_base_64("SGVsbG8gd29ybGQ=") 
| fields event_id, decoded_string 
| limit 3 

Explanation: This query adds a new field, decoded_string, which contains the plain text "Hello world" after decoding the provided base64 literal.

Output:

EVENT_ID DECODED_STRING
101 "Hello world"
102 "Hello world"
103 "Hello world"

Example 2: Decoding a literal base64 string representing JSON

Goal: Decode a base64 string that, when decoded, forms a JSON object. This shows the ability to handle more complex string content.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter decoded_json_string = convert_from_base_64("eyJsZXZlbCI6ICJpbmZvIiwgIm1lc3NhZ2UiOiAiU3lzdGVtIHN0YXJ0ZWQifQ==") 
| fields event_id, decoded_json_string 
| limit 3 

Explanation: The query decodes the base64 string, resulting in a JSON-formatted string as the value for decoded_json_string.

Output:

EVENT_ID DECODED_JSON_STRING
101 "{"level": "info", "message": "System started"}"
102 "{"level": "info", "message": "System started"}"
103 "{"level": "info", "message": "System started"}"

Example 3: Handling an invalid base64 input

Goal: Illustrate the function's behavior when provided with a string that is not a valid base64 encoding.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter invalid_decode_attempt = convert_from_base_64("NotABase64String!") 
| fields event_id, invalid_decode_attempt 
| limit 3 

Explanation: When the input string is not a valid base64 format, the convert_from_base_64() function still attempts to return the decoded string.

Output:

EVENT_ID INVALID_DECODE_ATTEMPT
101 "6@\u0005\u001e넭)"
102 "6@\u0005\u001e넭)"
103 "6@\u0005\u001e넭)"

Example 4: Decoding an empty base64 string

Goal: Show the result of decoding an empty base64 string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter decoded_empty_string = convert_from_base_64("") 
| fields event_id, decoded_empty_string 
| limit 3 

Explanation: Decoding an empty base64 string results in an empty string.

Output:

EVENT_ID DECODED_EMPTY_STRING
101 ""
102 ""
103 ""

convert_to_base_64

Use the convert_to_base_64() function to encode an input string into its base64 representation.

Syntax

convert_to_base_64 ("<input_string>")

Parameters

Name Type Required Description
input_string string Yes The string to be base64 encoded.

Returns

The convert_to_base_64() function returns the base64-encoded value as a string.

Usage notes

  • The function requires a single string input.
  • The function will return NULL if the input is NULL.
  • This function is typically employed within the alter stage to create new fields or modify existing ones by encoding string data.
  • The function can also be used within a filter stage if the encoding result is needed for a conditional check.

Examples

Example 1: Encoding a basic literal string

Goal: Encode a simple literal string to its base64 representation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter encoded_string = convert_to_base_64("Hello world")
| fields event_id, encoded_string
| limit 3

Explanation: This query adds a new field, encoded_string, which contains the base64-encoded representation of "Hello world".

Output:

EVENT_ID ENCODED_STRING
101 SGVsbG8gd29ybGQ=
102 SGVsbG8gd29ybGQ=
103 SGVsbG8gd29ybGQ=

Example 2: Encoding an existing string field

Goal: Encode values from an existing string field in the dataset.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter encoded_description = convert_to_base_64(event_description)
| fields event_id, event_description, encoded_description
| limit 3

Explanation: The query creates encoded_description by applying base64 encoding to the event_description field for each record.

Output:

EVENT_ID EVENT_DESCRIPTION ENCODED_DESCRIPTION
101 User login successful VXNlciBsb2dpbiBzdWNjZXNzZnVs
102 File access attempt RmlsZSBhY2Nlc3MgYXR0ZW1wdA==
103 Network connection established TmV0d29yayBjb25uZWN0aW9uIGVzdGFibGlzaGVk

Example 3: Encoding a string derived from a numeric field

Goal: Convert a numeric field to a string and then encode it to base64.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_id_string = to_string(event_id)
| alter encoded_id = convert_to_base_64(event_id_string)
| fields event_id, event_id_string, encoded_id
| limit 3

Explanation: The event_id is first converted to a string using to_string(), and then convert_to_base_64() encodes this string.

Output:

EVENT_ID EVENT_ID_STRING ENCODED_ID
101 101 MTAx
102 102 MTAy
103 103 MTAz

Example 4: Encoding an empty string

Goal: Encode an empty string to observe the result.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter encoded_empty_string = convert_to_base_64("")
| fields event_id, encoded_empty_string
| limit 3

Explanation: Encoding an empty string results in an empty string.

Output:

EVENT_ID ENCODED_EMPTY_STRING
101  
102  
103  

Example 5: Handling NULL input

Goal: Observe the behavior when a NULL input is provided to the function.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter encoded_null_field = convert_to_base_64(dst_domain)
| fields event_id, dst_domain, encoded_null_field
| limit 5

Explanation: When the input to convert_to_base_64() is NULL (such as dst_domain for event 105), the function consistently returns NULL for the output field.

Output:

EVENT_ID DST_DOMAIN ENCODED_NULL_FIELD
101 ec2.amazonaws.com ZWMyLmFtYXpvbmF3cy5jb20=
102 sts.amazonaws.com c3RzLmFtYXpvbmF3cy5jb20=
103 www.google.com d3d3Lmdvb2dsZS5jb20=
104 dropbox.com ZHJvcGJveC5jb20=
105 NULL NULL

cos

Use the cos() function to calculate the cosine of a numeric value specified in radians.

Syntax

cos(<numeric_expression>)

Parameters

Name Type Required Description
numeric_expression integer, float Yes The angle in radians for which to calculate the cosine. Any real number is valid.

Returns

Type: float

Description: The cos() function returns the cosine of the input angle. The resulting value is within the range [-1, 1].

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The cos() function accepts any real number as input. The input is interpreted as an angle in radians.
  • Null Handling: If the input expression is null, the function returns null.
  • Degrees to Radians: If your input is in degrees, convert it to radians first by multiplying by PI() / 180.
  • Periodicity: The cosine function is periodic with period 2π, meaning cos(x) = cos(x + 2π).
  • Key Values: cos(0) returns 1.0, cos(π/2) returns 0.0, cos(π) returns -1.0.
  • Common Use Cases: This function is typically used within the alter stage for trigonometric calculations, signal processing, coordinate transformations, and distance computations.

Examples

Example 1: Calculate cosine of literal values

Goal: Calculate the cosine for specific radian values to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = cos(0), result2 = cos(pi()), result3 = cos(divide(pi(), 2))
| fields result1, result2, result3

Explanation: You use cos() on three different radian values. cos(0) returns 1.0, cos(π) returns -1.0, and cos(π/2) returns approximately 0.0 (a very small number close to zero due to floating-point precision).

Output:

RESULT1 RESULT2 RESULT3
1.0 -1.0 0.0

Example 2: Calculate cosine from a field value

Goal: Calculate the cosine of values stored in a dataset field, treating them as angles in radians.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter cos_val = cos(numeric_value)
| fields event_id, numeric_value, cos_val
| limit 3

Explanation: This query computes the cosine for each value in the numeric_value field and stores the result in cos_val. Since cos() accepts any real number, no filtering is needed.

Output:

EVENT_ID NUMERIC_VALUE COS_VAL
101 0.0 1.0
102 1.5708 0.0
103 3.14159 -1.0

Example 3: Use cosine in a distance calculation

Goal: Use the cosine function as part of a spherical distance calculation between two points.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter cos_angle = cos(duration_seconds)
| alter scaled_cos = multiply(cos_angle, numeric_value)
| fields event_id, duration_seconds, cos_angle, numeric_value, scaled_cos
| limit 3

Explanation: This query calculates the cosine of duration_seconds (treated as an angle in radians) and then multiplies the result by numeric_value to produce a scaled cosine value, which is a common pattern in coordinate transformations.

Output:

EVENT_ID DURATION_SECONDS COS_ANGLE NUMERIC_VALUE SCALED_COS
101 0.0 1.0 5.0 5.0
102 1.5708 0.0 3.0 0.0
103 3.14159 -1.0 7.0 -7.0

cosine_distance

Use the cosine_distance() function to calculate the cosine distance between two numeric vectors (arrays). The cosine distance measures the dissimilarity between two vectors based on the angle between them.

Syntax

cosine_distance(<vector1>, <vector2>)

Parameters

Name Type Required Description
vector1 array of integers or floats Yes The first numeric vector (array). Must have the same length as vector2.
vector2 array of integers or floats Yes The second numeric vector (array). Must have the same length as vector1.

Returns

Type: float

Description: The cosine_distance() function returns a value between 0 and 2, where 0 indicates identical direction, 1 indicates orthogonal (perpendicular) vectors, and 2 indicates opposite directions. If either input is null or the vectors have different lengths, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Vector Length: Both input vectors must have the same number of elements. If they differ in length, the function returns null.
  • Vector Type: Only float values are supported for the input vectors.
  • Null Handling: If either input expression is null, the function returns null.
  • Zero Vectors: If either vector is a zero vector (all elements are 0), the result is undefined and may return NaN or null.
  • Relationship to Cosine Similarity: Cosine distance = 1 - cosine similarity. A cosine distance of 0 means the vectors are identical in direction (cosine similarity of 1).
  • Common Use Cases: This function is typically used within the alter stage for similarity analysis, text comparison using embedding vectors, anomaly detection, and machine learning feature comparison.

Examples

Example 1: Calculate cosine distance between literal vectors

Goal: Calculate the cosine distance between specific vector pairs to verify known results.

XQL code:

dataset = xdr_data
| limit 1
| alter dist1 = cosine_distance(arraycreate(1.0, 0.0, 0.0), arraycreate(1.0, 0.0, 0.0))
| alter dist2 = cosine_distance(arraycreate(1.0, 0.0, 0.0), arraycreate(0.0, 1.0, 0.0))
| fields dist1, dist2

Explanation: cosine_distance of two identical vectors (1.0, 0.0, 0.0) returns 0.0 (no distance). The distance between (1.0, 0.0, 0.0) and (0.0, 1.0, 0.0) returns 1.0 since these vectors are orthogonal (perpendicular).

Output:

DIST1 DIST2
0.0 1.0

Example 2: Calculate cosine distance between field vectors

Goal: Compute the cosine distance between two array fields in a dataset.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_codes != null
| alter reference_vector = arraycreate(1.0, 2.0, 3.0, 4.0, 5.0)
| alter cos_dist = cosine_distance(numeric_codes, reference_vector)
| fields event_id, numeric_codes, cos_dist
| limit 3

Explanation: This query creates a reference vector and computes the cosine distance between each record's numeric_codes array and the reference vector. Lower values indicate the vectors point in a more similar direction.

Output:

EVENT_ID NUMERIC_CODES COS_DIST
101 [13, -47, 29, 82, -15] 0.8523
102 [-21, 56, 13, -88, 42] 1.3241
103 [90, -33, 7, 51, -62, 18] null

Example 3: Compare similarity of two records

Goal: Use cosine distance to determine how similar two records are based on their numeric arrays.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_codes != null and array_length(numeric_codes) = 5
| alter ref = arraycreate(10.0, 20.0, 30.0, 40.0, 50.0)
| alter distance = cosine_distance(numeric_codes, ref)
| alter similarity = subtract(1, distance)
| fields event_id, numeric_codes, distance, similarity
| sort asc distance
| limit 3

Explanation: This query computes the cosine distance between each record's numeric_codes and a reference vector, then derives the cosine similarity as 1 - distance. Results are sorted by distance ascending, showing the most similar records first.

Output:

EVENT_ID NUMERIC_CODES DISTANCE SIMILARITY
105 [8, 15, 25, 35, 45] 0.0123 0.9877
101 [13, -47, 29, 82, -15] 0.8523 0.1477
102 [-21, 56, 13, -88, 42] 1.3241 -0.3241

cot

Use the cot() function to calculate the cotangent of a numeric value specified in radians. The cotangent is the reciprocal of the tangent function.

Syntax

cot(<numeric_expression>)

Parameters

Name Type Required Description
numeric_expression integer, float Yes The angle in radians for which to calculate the cotangent. The value must not be zero or a multiple of π.

Returns

Type: float

Description: The cot() function returns the cotangent of the input angle, which is equivalent to cos(x) / sin(x) or 1 / tan(x). If the input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The cot() function accepts any real number except values where sin(x) = 0 (i.e., multiples of π), where the cotangent is undefined.
  • Undefined Values: At x = 0 and multiples of π, the function returns infinity, NaN, or null since the cotangent is undefined at these points.
  • Null Handling: If the input expression is null, the function returns null.
  • Relationship: cot(x) = 1 / tan(x) = cos(x) / sin(x).
  • Common Use Cases: This function is typically used within the alter stage for trigonometric calculations, geometric analysis, and engineering computations.

Examples

Example 1: Calculate cotangent of literal values

Goal: Calculate the cotangent for specific radian values to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = cot(divide(pi(), 4)), result2 = cot(divide(pi(), 2)), result3 = cot(1)
| fields result1, result2, result3

Explanation: You use cot() on three different radian values. cot(π/4) returns 1.0 (since tan(π/4) = 1), cot(π/2) returns approximately 0.0 (since tan(π/2) approaches infinity), and cot(1) returns approximately 0.6421.

Output:

RESULT1 RESULT2 RESULT3
1.0 0.0 0.64209

Example 2: Calculate cotangent from a field value

Goal: Calculate the cotangent of values stored in a dataset field, filtering out values near zero.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value != 0
| alter cot_val = cot(numeric_value)
| fields event_id, numeric_value, cot_val
| limit 3

Explanation: This query filters out zero values (where cotangent is undefined), then computes the cotangent for each remaining value in the numeric_value field.

Output:

EVENT_ID NUMERIC_VALUE COT_VAL
101 0.5 1.83049
102 1.0 0.64209
103 2.0 -0.45766

Example 3: Verify cotangent as reciprocal of tangent

Goal: Demonstrate that cot(x) equals 1 / tan(x) by computing both and comparing.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value != 0
| alter cot_val = cot(numeric_value)
| alter reciprocal_tan = divide(1, tan(numeric_value))
| fields event_id, numeric_value, cot_val, reciprocal_tan
| limit 3

Explanation: This query computes both cot(numeric_value) and 1 / tan(numeric_value) to verify they produce the same result, confirming the mathematical identity cot(x) = 1 / tan(x).

Output:

EVENT_ID NUMERIC_VALUE COT_VAL RECIPROCAL_TAN
101 0.5 1.83049 1.83049
102 1.0 0.64209 0.64209
103 2.0 -0.45766 -0.45766

count_distinct

Use the count_distinct() function within a comp stage to calculate and return a single count representing the number of unique values found for a specified field over a group of rows.

Syntax

SQL

comp count_distinct() [as ] [by [,...]] [addrawdata = true false [as ]]

Parameters

Name Type Required Description
field string, integer, float, boolean Yes The field for which you want to count the number of unique values.
alias string No The alias name for the output column, assigned using the as clause.
field1, field2 string, integer, float, boolean No The field(s) used to partition the data into distinct groups via the by clause.
addrawdata boolean No When set to true, introduces a raw data column into the output that lists the raw data events contributing to the aggregate result.
target field string No The alias name for the raw data column when addrawdata is set to true.

Returns

The count_distinct() function returns a single numerical count value representing the number of unique values found for the specified field.

Usage Notes

  • Use count_distinct() to retrieve the number of unique values in the result set, whereas the count() function retrieves the total number of values.
  • The comp stage must always precede an aggregate or approximate aggregate function like count_distinct().
  • New columns generated by the comp stage (including the distinct count itself) are typically appended as the last columns in the result set.
  • Any other fields not explicitly included in the by clause or as part of a calculated column will be removed from the result set, including all system fields like _time.
  • When addrawdata is set to true, the query processes up to 50 defined fields and displays up to 100 events.

Examples

Example 1: Counting Unique Values of a Specific Field Across the Entire Dataset (No Grouping)

Goal: Calculate the total number of unique event_description values in the sample_xql_raw dataset.

XQL Code:

SQL

config timeframe = 1d\
| dataset = sample_xql_raw\
| comp count_distinct(event_description) as unique_event_descriptions\
| limit 1

Explanation: This query computes the number of unique event_description values present in the sample_xql_raw dataset and names the resulting field unique_event_descriptions. Since all event_description values in sample_xql_raw are distinct, the count will be the total number of events.

Output:

unique_event_descriptions
10

**Example 2: Counting Unique Values of a Specific Field, Grouped by Another Field

Goal: Calculate the number of unique event_description values separately for successful (true) and unsuccessful (false) events.

XQL Code:

SQL

config timeframe = 1d\
| dataset = sample_xql_raw\
| comp count_distinct(event_description) as unique_descriptions_by_status by is_successful\
| limit 3

Explanation: The query groups records by their is_successful status and then counts the unique event_description values for each group, presenting the counts in the unique_descriptions_by_status field. As all event_description values in sample_xql_raw are unique, this effectively counts the number of events in each is_successful group.

Output:

is_successful unique_descriptions_by_status
true 7
false 3

Example 3: Counting Unique Values of a Derived Field (JSON Extraction and Conversion)

Goal: Calculate the count of unique numerical values (derived from code or error_code fields within simple_json_data) after extraction and conversion.

XQL Code:

SQL

config timeframe = 1d\
| dataset = sample_xql_raw\
| alter json_code_number = to_number(coalesce(simple_json_data -> code, simple_json_data -> error_code))\
| comp count_distinct(json_code_number) as unique_codes_count\
| limit 1

Explanation: This query first extracts a numerical code or error_code from simple_json_data using syntactic sugar and coalesce(). to_number() converts the extracted string to a numerical type. Then, count_distinct() counts how many unique numerical codes were found across the dataset. Based on sample_xql_raw, event ID 101 has code 200, and event ID 109 has error_code 429, resulting in 2 unique values.

Output:

unique_codes_count
2

Example 4: Counting Unique Numerical Values from an Array Element

Goal: Count the number of unique first elements from the numeric_codes array.

XQL Code:

SQL

config timeframe = 1d\
| dataset = sample_xql_raw\
| alter first_numeric_val = arrayindex(numeric_codes, 0)\
| comp count_distinct(first_numeric_val) as unique_first_numeric_values\
| limit 1

Explanation: This query uses arrayindex() to access the first numeric code from the numeric_codes array for each record. The count_distinct() function then calculates the number of unique non-null values found among these extracted first elements.

Output:

unique_first_numeric_values
9

Example 5: Counting Unique Values with Raw Data Inclusion (addrawdata=true)

Goal: Include the raw events that contribute to each unique count using the addrawdata = true option.

XQL Code:

SQL

config timeframe = 1d\
| dataset = sample_xql_raw\
| comp count_distinct(event_id) as distinct_ids by is_successful addrawdata = true as raw_events_for_distinct_count\
| limit 3

Explanation: This query calculates the unique count of event_id values grouped by is_successful. Additionally, addrawdata = true generates a new column named raw_events_for_distinct_count, which contains a JSON representation of the raw events that contributed to each computed distinct count.

Output:

distinct_ids is_successful raw_events_for_distinct_count
7 true JSON representation of raw data for 7 unique events (truncated)
3 false JSON representation of raw data for 3 unique events (truncated)

count (comp)

Use the count() function within a comp stage to calculate and return the number of non-null values found for a specified field, or the total number of rows if no field is specified, over a group of rows.

Syntax

comp count([ <field>]) [as <alias>] [by <field1>[,<field2>...]] [addrawdata = true|false [as <target field>]]

Parameters

Name Type Required Description
field string, integer, float, boolean No The field to count non-null values for.
alias string No The alias name of the field, using the as clause.
field1, field2 string, integer, float, boolean No The fields used to group rows in the by clause.
addrawdata boolean No When set to true, introduces a raw_data column into the output, listing the raw data events that contributed to the aggregate result.
target field string No The alias name for the raw data column when addrawdata is set to true.

Returns

The count() function returns a single count value representing the number of rows or non-null values.

Usage Notes

  • When a field is provided, it exclusively counts non-null values.
  • Without a specified field, it calculates and returns the total number of rows, including those with null values.
  • The comp stage or the windowcomp stage must always precede count().
  • New columns generated by the comp stage (including the count itself) are typically appended as the last columns in the result set.
  • Any other fields not explicitly included in the by clause or as part of a calculated column will be removed from the result set, including all system fields.
  • When addrawdata is true, the query processes up to 50 defined fields and displays up to 100 events.

Examples

Example 1: Counting all rows across the entire dataset (no field specified)

Goal: Compute the total number of records in the sample_xql_raw dataset, including any with null values, without applying any grouping.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| comp count() as total_events 

Explanation: This query computes the total number of rows in the sample_xql_raw dataset and names the resulting field total_events. Because no field is specified in count(), it includes all rows.

Output:

total_events
10
 

Example 2: Counting non-null values of a specific field across the entire dataset (field specified)

Goal: Count the number of records where the event_description field is not null, across the entire dataset.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| comp count(event_description) as non_null_descriptions 

Explanation: The query calculates the count of records that have a non-null event_description value in the sample_xql_raw dataset.

Output:

non_null_descriptions
10
 

Example 3: Counting non-null values of a specific field, grouped by another field

Goal: Calculate the count of event_id occurrences separately for successful (true) and unsuccessful (false) events.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| comp count(event_id) as events_by_status by is_successful 

Explanation: The query groups records by their is_successful status and then counts the non-null event_id for each group, presenting the counts in the events_by_status field.

Output:

is_successful events_by_status
true 7
false 3
   

Example 4: Counting a value extracted and converted from JSON data

Goal: Calculate the count of records where a numerical value (from code or error_code) is successfully extracted from a JSON field (simple_json_data) and converted to a number.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| alter json_code_number = to_number(coalesce(simple_json_data \-> code, simple_json_data \-> error_code)   
| comp count(json_code_number) as count_of_codes   
| limit 1 

Explanation: This query first attempts to extract either the code or error_code from the simple_json_data field using json_extract_scalar(), with coalesce() handling potential nulls. to_number() then converts these to a numerical type. Finally, count() computes the number of these converted numerical values, automatically excluding records where the extraction resulted in a null value.

Output:

count_of_codes
2
 

Example 5: Counting a numerical value from an array element

Goal: Count events where a specific numeric element from an array field (numeric_codes) exists and is not null.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| alter first_numeric_val = arrayindex(numeric_codes, 0)   
| comp count(first_numeric_val) as count_first_numeric_code   
| limit 1 

Explanation: This query uses arrayindex() to access the first numeric code from the numeric_codes array for each record. The count() function then calculates the number of records where this extracted value is not null, excluding records where the array is empty (like event ID 104) or the first element is null.

Output:

count_first_numeric_code
9
 

Example 6: Counting with raw data inclusion (addrawdata=true)

Goal: Include the raw events that contribute to each count by using the addrawdata = true option.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| comp count(event_id) by is_successful addrawdata = true as raw_events_for_count   
| limit 3 

Explanation: Similar to Variant 3, this query calculates the count of event_id grouped by is_successful. Additionally, addrawdata = true generates a new column named raw_events_for_count, which contains a JSON representation of the raw events that contributed to each computed count.

Output:

is_successful raw_events_for_count
true JSON representation of raw data for 7 events (truncated)
false JSON representation of raw data for 3 events (truncated)
   

count (windowcomp)

Use the count() function within a windowcomp stage to calculate and return a single count value over a defined window or group of rows, presenting the result for each row within that window frame. The function can operate by counting non-null values for a specified field, or by counting the total number of rows within the window when no field is specified.

Syntax

windowcomp count([<field>]) [by <field> [,<field>,...]] [sort [asc|desc] <field1> [, [asc|desc] <field2>,...]] [between 0|null|<number>|-<number> [and 0|null|<number>|-<number>] [frame_type=range]] [as <alias>] 

Parameters

Name Type Required Description
field string, integer, float, boolean No The field to count non-null values for. If omitted, the function counts all rows including those with null values.
by field string, integer, float, boolean No The field(s) used to partition the data into distinct groups, allowing the function to operate independently within each partition.
sort field string, integer, float, boolean No Defines the order of rows within each partition, which is critical when defining a window using the between clause.
between boundaries integer, null No Defines the "window frame" (a specific range of rows) relative to the current row over which the count is calculated.
frame_type string No Specifies whether the window is defined by rows (the default) or a value range.
alias string No The alias name for the output column, assigned using the as clause.

Returns

The count() function returns a single numerical value representing the count. This value is displayed in a new column for every row in the group of rows it applies to.

Usage Notes

  • Defining a field for count() is optional; providing a field counts non-null values, while omitting it counts total rows.
  • The by clause partitions the data into distinct groups, and the count() function operates independently within each of these partitions, meaning a separate count is computed for each group.
  • The sort clause is mandatory for many windowcomp functions as it defines the order of rows within each partition, which is critical when defining a window using the between clause.
  • If the between clause is omitted, the default window used is typically the entire partition.
  • The windowcomp stage must always precede analytic functions like count() that calculate statistics.
  • Only one function can be defined per field within a single windowcomp stage.

Examples

Example 1: Over an entire partition (no field specified - counts all rows)

Goal: Calculate the total number of events (rows) for all events within each is_successful group, replicating this total count value for every row in that group.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| fields event_id, _time, is_successful, event_description   
| windowcomp count() by is_successful sort asc _time as count_in_partition   
| sort asc is_successful, asc _time, asc event_id   
| limit 10 

Explanation: For the false partition, there are 3 rows, so the count_in_partition is 3 for each row in this partition. For the true partition, there are 7 rows, so the count is 7 for each row in that partition.

Output:

_time event_id is_successful event_description count_in_partition
2023-10-26 10:05:30 UTC 102 false "File access attempt" 3
2023-10-26 10:40:10 UTC 106 false "Unauthorized access detected" 3
2023-10-26 10:55:55 UTC 109 false "API request throttled" 3
2023-10-26 10:00:00 UTC 101 true "User login successful" 7
2023-10-26 10:15:15 UTC 103 true "Network connection established" 7
2023-10-26 10:20:00 UTC 104 true "System heartbeat" 7
2023-10-26 10:30:45 UTC 105 true "Data transformation" 7
2023-10-26 10:45:00 UTC 107 true "Cloud resource modification" 7
2023-10-26 10:50:20 UTC 108 true "Software update initiated" 7
2023-10-26 11:00:10 UTC 110 true "Database backup completed" 7

Example 2: Over an entire partition (field specified - counts non-null values)

Goal: Calculate the count of event_description specifically to count non-null occurrences of that field for all events within each is_successful group.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| fields event_id, _time, is_successful, event_description   
| windowcomp count(event_description) by is_successful as non_null_count_in_partition   
| sort asc is_successful, asc _time, asc event_id   
| limit 10 

Explanation: Because all event_description values in the dataset are non-null, the counts for each partition are identical to counting all rows (3 for false and 7 for true), explicitly demonstrating that count(field) only counts non-null instances.

Output:

_time event_id is_successful event_description non_null_count_in_partition
2023-10-26 10:05:30 UTC 102 false "File access attempt" 3
2023-10-26 10:40:10 UTC 106 false "Unauthorized access detected" 3
2023-10-26 10:55:55 UTC 109 false "API request throttled" 3
2023-10-26 10:00:00 UTC 101 true "User login successful" 7
2023-10-26 10:15:15 UTC 103 true "Network connection established" 7
2023-10-26 10:20:00 UTC 104 true "System heartbeat" 7
2023-10-26 10:30:45 UTC 105 true "Data transformation" 7
2023-10-26 10:45:00 UTC 107 true "Cloud resource modification" 7
2023-10-26 10:50:20 UTC 108 true "Software update initiated" 7
2023-10-26 11:00:10 UTC 110 true "Database backup completed" 7

Example 3: Rolling window (preceding 2 rows and current row, no field)

Goal: Count the total number of rows within the current row and the two immediately preceding rows within its partition, operating on a sliding window ordered by _time.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| fields event_id, _time, is_successful, event_description   
| windowcomp count() by is_successful sort asc _time between -2 and 0 as rolling_count   
| sort asc is_successful, asc _time, asc event_id   
| limit 10 

Explanation: The between -2 and 0 clause defines a window including the current row and the two preceding rows. For each row, the count() calculates the number of rows within its specific rolling window, starting from the beginning of the partition if there are not enough preceding rows.

Output:

_time event_id is_successful event_description rolling_count
2023-10-26 10:05:30 UTC 102 false "File access attempt" 1
2023-10-26 10:40:10 UTC 106 false "Unauthorized access detected" 2
2023-10-26 10:55:55 UTC 109 false "API request throttled" 3
2023-10-26 10:00:00 UTC 101 true "User login successful" 1
2023-10-26 10:15:15 UTC 103 true "Network connection established" 2
2023-10-26 10:20:00 UTC 104 true "System heartbeat" 3
2023-10-26 10:30:45 UTC 105 true "Data transformation" 3
2023-10-26 10:45:00 UTC 107 true "Cloud resource modification" 3
2023-10-26 10:50:20 UTC 108 true "Software update initiated" 3
2023-10-26 11:00:10 UTC 110 true "Database backup completed" 3

Example 4: Fixed window (current row to unbounded following, field specified)

Goal: Calculate the count of non-null event_description values for each row over a fixed window that starts from the current row and extends to the end of its partition.

XQL Code:

config timeframe = 1d   
| dataset = sample_xql_raw   
| fields event_id, _time, is_successful, event_description   
| windowcomp count(event_description) by is_successful sort asc _time between 0 and null as count_from_current   
| sort asc is_successful, asc _time, asc event_id   
| limit 10 

Explanation: The between 0 and null clause defines a window starting from the current row to the very end of the partition. count(event_description) dynamically updates for each row to represent the remaining number of non-null values.

Output:

_time event_id is_successful event_description count_from_current
2023-10-26 10:05:30 UTC 102 false "File access attempt" 3
2023-10-26 10:40:10 UTC 106 false "Unauthorized access detected" 2
2023-10-26 10:55:55 UTC 109 false "API request throttled" 1
2023-10-26 10:00:00 UTC 101 true "User login successful" 7
2023-10-26 10:15:15 UTC 103 true "Network connection established" 6
2023-10-26 10:20:00 UTC 104 true "System heartbeat" 5
2023-10-26 10:30:45 UTC 105 true "Data transformation" 4
2023-10-26 10:45:00 UTC 107 true "Cloud resource modification" 3
2023-10-26 10:50:20 UTC 108 true "Software update initiated" 2
2023-10-26 11:00:10 UTC 110 true "Database backup completed" 1

csc

Use the csc() function to calculate the cosecant of a numeric value specified in radians. The cosecant is the reciprocal of the sine function.

Syntax

csc(<numeric_expression>)

Parameters

Name Type Required Description
numeric_expression integer, float Yes The angle in radians for which to calculate the cosecant. The value must not be zero or a multiple of π.

Returns

Type: float

Description: The csc() function returns the cosecant of the input angle, which is equivalent to 1 / sin(x). If the input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The csc() function accepts any real number except values where sin(x) = 0 (i.e., multiples of π, including zero), where the cosecant is undefined.
  • Undefined Values: At x = 0 and multiples of π, the function returns infinity, NaN, or null since the cosecant is undefined at these points.
  • Null Handling: If the input expression is null, the function returns null.
  • Relationship: csc(x) = 1 / sin(x).
  • Result Range: The result is always ≤ -1 or ≥ 1 (never between -1 and 1).
  • Common Use Cases: This function is typically used within the alter stage for trigonometric calculations, wave analysis, and engineering computations.

Examples

Example 1: Calculate cosecant of literal values

Goal: Calculate the cosecant for specific radian values to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = csc(divide(pi(), 2)), result2 = csc(divide(pi(), 6)), result3 = csc(1)
| fields result1, result2, result3

Explanation: You use csc() on three different radian values. csc(π/2) returns 1.0 (since sin(π/2) = 1), csc(π/6) returns 2.0 (since sin(π/6) = 0.5), and csc(1) returns approximately 1.1884.

Output:

RESULT1 RESULT2 RESULT3
1.0 2.0 1.18840

Example 2: Calculate cosecant from a field value

Goal: Calculate the cosecant of values stored in a dataset field, filtering out values near zero.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value != 0
| alter csc_val = csc(numeric_value)
| fields event_id, numeric_value, csc_val
| limit 3

Explanation: This query filters out zero values (where cosecant is undefined), then computes the cosecant for each remaining value in the numeric_value field.

Output:

EVENT_ID NUMERIC_VALUE CSC_VAL
101 0.5 2.08583
102 1.0 1.18840
103 1.5708 1.0

Example 3: Verify cosecant as reciprocal of sine

Goal: Demonstrate that csc(x) equals 1 / sin(x) by computing both and comparing.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value != 0
| alter csc_val = csc(numeric_value)
| alter reciprocal_sin = divide(1, sin(numeric_value))
| fields event_id, numeric_value, csc_val, reciprocal_sin
| limit 3

Explanation: This query computes both csc(numeric_value) and 1 / sin(numeric_value) to verify they produce the same result, confirming the mathematical identity csc(x) = 1 / sin(x).

Output:

EVENT_ID NUMERIC_VALUE CSC_VAL RECIPROCAL_SIN
101 0.5 2.08583 2.08583
102 1.0 1.18840 1.18840
103 1.5708 1.0 1.0

current_time

Use the current_time() function to return a TIMESTAMP value representing the exact time at which the query is executed.

Syntax

current_time()

Parameters

The current_time() function does not accept any parameters.

Returns

The current_time() function returns a TIMESTAMP representing the current system time.

Usage notes

  • The functıon is particularly useful for calculations that require the current moment as a reference point, such as determining the age of an event or establishing dynamic time windows.
  • The function returns the timestamp according to the default Timestamp Format specified in Server Settings.
  • The function is typically used within the alter or filter stages.

Examples

Example 1: Assigning current_time() to a new field in an alter stage

Goal: Create a new field that holds the current timestamp for each record.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter current_query_timestamp = current_time() 
| fields event_id, current_query_timestamp 
| limit 3

Explanation: The current_time() function provides the exact time of query execution, which is then assigned to the current_query_timestamp field for every record.

Output:

EVENT_ID CURRENT_QUERY_TIMESTAMP
101 Jul 25th 2024 14:30:00
102 Jul 25th 2024 14:30:00
103 Jul 25th 2024 14:30:00

Example 2: Using current_time() within timestamp_diff() in a filter stage

Goal: Filter events that occurred more than 1 day ago relative to the current time.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter timestamp_diff(current_time(), _time, "DAY") > 1 
| fields event_id, _time 
| limit 3

Explanation: The timestamp_diff() function calculates the difference between the current time and the event's _time. The filter stage retains only those records where the difference is greater than 1 day.

Output:

EVENT_ID _TIME
101 Oct 26th 2023 10:00:00
102 Oct 26th 2023 10:05:30
103 Oct 26th 2023 10:15:15

Example 3: Using current_time() within date_floor() in an alter Stage

Goal: Round the current time down to the nearest week.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter current_week_start = date_floor(current_time(), "w") 
| fields event_id, current_week_start 
| limit 3

Explanation: The date_floor() function, when applied to current_time(), truncates the timestamp to the beginning of the specified unit ("w" for week).

Output:

EVENT_ID CURRENT_WEEK_START
101 Jul 21st 2024 00:00:00
102 Jul 21st 2024 00:00:00
103 Jul 21st 2024 00:00:00

Example 4: Using current_time() within extract_time() in an alter Stage

Goal: Extract a specific part of the current time, such as the hour.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter current_hour = extract_time(current_time(), "HOUR") 
| fields event_id, current_hour 
| limit 3

Explanation: The extract_time() function isolates the "HOUR" component from the current timestamp provided by current_time(), returning an integer representing that hour.

Output:

EVENT_ID CURRENT_HOUR
101 14
102 14
103 14

Example 5: Filtering events based on process execution age

Goal: From the xdr_data dataset, retrieve events from the last 24 hours where the actor process began running more than 30 days prior to the current time.

XQL Code:

dataset = sample_xql_raw
| filter timestamp_diff(current_time(), to_timestamp(actor_process_execution_time, "MILLIS"), "DAY") > 30

Explanation: This query uses current_time() to get the present timestamp and to_timestamp() to convert the actor_process_execution_time (stored in milliseconds) into a standard timestamp format. The timestamp_diff() function then calculates the number of days between these two points. The filter stage ensures only records with a difference greater than 30 days are returned.

Output

event_id actor_process_execution_time _time calculated_days_old
5521 1693569600000 2023-10-15 08:30:00 UTC 44
5589 1691064000000 2023-10-15 09:12:00 UTC 73
5602 1688212800000 2023-10-15 10:05:00 UTC 106

date_floor

Use the date_floor() function to return a new timestamp that is rounded down to the nearest whole value of a specified time unit.

Syntax

date_floor (<timestamp field>, "<time_unit>" [, "<time zone>"])

Parameters

Name Type Required Description
timestamp field timestamp Yes The input timestamp value, originating from a field or the result of another function.
time_unit string Yes The unit to which the timestamp should be rounded down. Supported values are y (year), mo (month), w (week), d (day), or h (hour). This parameter is not case-sensitive.
time_zone string No The time zone to apply for the calculation. This can be an hours offset (for example, +08:00) or a time zone name from the List of Supported Time Zones, (for example, America/Chicago). If omitted, the default is UTC.

Returns

The date_floor() function returns a TIMESTAMP value rounded down to the beginning of the specified time unit.

Usage notes

  • The function always rounds the timestamp down to the beginning of the specified time unit. For example, date_floor("2023-10-26 10:30:00 UTC", "h") results in 2023-10-26 10:00:00.
  • Supported time units (y, mo, w, d, h) are not case-sensitive.
  • This function is typically used within alter or filter stages to perform data transformations for time-based aggregation or analysis.

Examples

Example 1: Rounding _time to the nearest hour (default UTC)

Goal: Round the _time field to the beginning of the hour using the default UTC time zone.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter floored_hour = date_floor(_time, "h") 
| fields event_id, _time, floored_hour 
| limit 3 

Explanation: The query rounds the _time field down to the start of the hour. For events occurring at 10:05:30 or 10:15:15, the result is 10:00:00.

Output:

event_id _time floored_hour
101 Oct 26th 2023 10:00:00 Oct 26th 2023 10:00:00
102 Oct 26th 2023 10:05:30 Oct 26th 2023 10:00:00
103 Oct 26th 2023 10:15:15 Oct 26th 2023 10:00:00

Example 2: Rounding _time to the nearest day (default UTC)

Goal: Round the _time field to the beginning of the day using the default UTC time zone.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter floored_day = date_floor(_time, "d") 
| fields event_id, _time, floored_day 
| limit 3 

Explanation: All _time values are rounded down to the very beginning of the day (midnight UTC) on October 26th, 2023.

Output:

event_id _time floored_day
101 Oct 26th 2023 10:00:00 Oct 26th 2023 00:00:00
102 Oct 26th 2023 10:05:30 Oct 26th 2023 00:00:00
103 Oct 26th 2023 10:15:15 Oct 26th 2023 00:00:00

Example 3: Rounding _time to the nearest week (default UTC)

Goal: Round the _time field to the beginning of the week using the default UTC time zone.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter floored_week = date_floor(_time, "w") 
| fields event_id, _time, floored_week 
| limit 3 

Explanation: Because October 26th, 2023, falls within the week starting Sunday, October 22nd, 2023, all timestamps are rounded down to the beginning of that week (midnight UTC on October 22nd).

Output:

event_id _time floored_week
101 Oct 26th 2023 10:00:00 Oct 22nd 2023 00:00:00
102 Oct 26th 2023 10:05:30 Oct 22nd 2023 00:00:00
103 Oct 26th 2023 10:15:15 Oct 22nd 2023 00:00:00

Example 4: Rounding _time to the nearest month (default UTC)

Goal: Round the _time field to the beginning of the month using the default UTC time zone.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter floored_month = date_floor(_time, "mo") 
| fields event_id, _time, floored_month 
| limit 3 

Explanation: All _time values are rounded down to the very beginning of October 2023 (midnight UTC on October 1st).

Output:

event_id _time floored_month
101 Oct 26th 2023 10:00:00 Oct 1st 2023 00:00:00
102 Oct 26th 2023 10:05:30 Oct 1st 2023 00:00:00
103 Oct 26th 2023 10:15:15 Oct 1st 2023 00:00:00

Example 5: Rounding _time to the nearest year (default UTC)

Goal: Round the _time field to the beginning of the year using the default UTC time zone.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter floored_year = date_floor(_time, "y") 
| fields event_id, _time, floored_year 
| limit 3 

Explanation: All _time values are rounded down to the very beginning of 2023 (midnight UTC on January 1st).

Output:

event_id _time floored_year
101 Oct 26th 2023 10:00:00 Jan 1st 2023 00:00:00
102 Oct 26th 2023 10:05:30 Jan 1st 2023 00:00:00
103 Oct 26th 2023 10:15:15 Jan 1st 2023 00:00:00

Example 6: Rounding current_time() to the nearest day with a specific time zone

Goal: Round the current time (assumed here as Jul 25th 2024 14:30:00 UTC) to the start of the day in a specific time zone ("America/Los_Angeles").

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter current_time_val = current_time() 
| alter floored_day_la = date_floor(current_time_val, "d", "America/Los_Angeles") 
| fields event_id, current_time_val, floored_day_la 
| limit 3 

Explanation: The function converts the UTC time to the Los Angeles time zone (PDT, UTC-7), rounds it to the start of that day (midnight PDT), and returns the timestamp. Midnight PDT translates to 07:00:00 UTC.

Output:

event_id current_time_val floored_day_la
101 Jul 25th 2024 14:30:00 Jul 25th 2024 07:00:00
102 Jul 25th 2024 14:30:00 Jul 25th 2024 07:00:00
103 Jul 25th 2024 14:30:00 Jul 25th 2024 07:00:00

Example 7: Filtering events relative to the start of the week with timezone adjustment

Goal: Return up to 100 records from the xdr_data dataset where the event time (_time) is earlier than a calculated timestamp. The calculation determines the start of the current week in the "America/Los_Angeles" timezone and subtracts exactly 24 days (2,073,600 seconds) from that point.

XQL Code:

dataset = sample_xql_raw
| filter _time < to_timestamp(add(to_epoch(date_floor(current_time(),"w", "America/Los_Angeles")),-2073600))
| limit 100

Explanation: This query performs a multi-step time transformation to create a dynamic filter:

  1. current_time() retrieves the present time.
  2. date_floor(..., "w", "America/Los_Angeles") rounds that time down to the beginning of the week based on Los Angeles time.
  3. to_epoch(...) converts that "start of week" timestamp into a Unix epoch integer (seconds).
  4. add(..., -2073600) subtracts 2,073,600 seconds (equivalent to 24 days) from the epoch value.
  5. to_timestamp(...) converts the resulting integer back into a standard timestamp format.
  6. The filter stage then compares the _time of every record against this calculated value.

Output:

_time event_id event_type
2023-10-01 14:20:00 UTC 88412 ENHANCED_EVENT_LOG
2023-09-28 09:15:30 UTC 88305 STORYLINE
2023-09-25 22:10:00 UTC 88112 BROWSER_QUERY

div

Use the div() function to perform integer division of two numeric values, returning the quotient without the remainder.

Syntax

div(<dividend>, <divisor>)

Parameters

Name Type Required Description
dividend integer, float Yes The number to be divided (numerator).
divisor integer, float Yes The number to divide by (denominator). Must not be zero.

Returns

Type: integer

Description: The div() function returns the integer quotient of the division, truncating any fractional part. If either input is null or the divisor is zero, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Integer Division: Unlike the divide() function which returns a float, div() performs integer (truncated) division and returns only the whole number part of the quotient.
  • Division by Zero: If the divisor is zero, the function returns null or raises an error.
  • Null Handling: If either input expression is null, the function returns null.
  • Truncation Direction: The result is truncated toward zero. For example, div(7, 2) returns 3 and div(-7, 2) returns -3.
  • Common Use Cases: This function is typically used within the alter stage for bucketing values, calculating whole units, and performing modular arithmetic in combination with mod().

Examples

Example 1: Integer division of literal values

Goal: Perform integer division on specific numeric literals to verify the truncation behavior.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = div(10, 3), result2 = div(7, 2), result3 = div(-7, 2)
| fields result1, result2, result3

Explanation: div(10, 3) returns 3 (10 ÷ 3 = 3 remainder 1), div(7, 2) returns 3 (7 ÷ 2 = 3 remainder 1), and div(-7, 2) returns -3 (truncated toward zero).

Output:

RESULT1 RESULT2 RESULT3
3 3 -3

Example 2: Integer division from field values

Goal: Use integer division to calculate how many complete hours are in a duration measured in seconds.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter complete_hours = div(to_integer(multiply(duration_seconds, 3600)), 3600)
| fields event_id, duration_seconds, complete_hours
| limit 3

Explanation: This query converts duration_seconds to a larger value and then uses div() to find the number of complete hours, discarding any fractional hour.

Output:

EVENT_ID DURATION_SECONDS COMPLETE_HOURS
101 7200.5 7200
102 3661.0 3661
103 1800.0 1800

Example 3: Combine div with mod for quotient and remainder

Goal: Use div() and mod() together to decompose a value into its quotient and remainder.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter quotient = div(event_id, 10)
| alter remainder = mod(event_id, 10)
| fields event_id, quotient, remainder
| limit 3

Explanation: This query decomposes event_id into its quotient and remainder when divided by 10. For example, event_id 101 gives quotient 10 and remainder 1, since 101 = 10 × 10 + 1.

Output:

EVENT_ID QUOTIENT REMAINDER
101 10 1
102 10 2
103 10 3

divide

Use the divide() function to perform arithmetic division, calculating the quotient of two numbers.

Syntax

divide (<string> | <integer> | <float>, <string> | <integer> | <float>)

Parameters

Name Type Required Description
numerator integer, float, string Yes The number to be divided (the dividend).
denominator integer, float, string Yes The number by which to divide (the divisor).

Returns

The divide() function returns the numerical quotient of the two input parameters.

Usage notes

  • The function accepts numeric literals (integers, floating-point numbers) or field values that represent numbers.
  • The function supports input values provided as a string data type (for example, an integer stored as a string).
  • The function can handle negative numbers.
  • This function is typically used within the alter stage to create or modify fields.

Examples

Example 1: Dividing an integer field by an integer literal

Goal: Calculate a new field by dividing an existing integer field (event_id) by a fixed integer value.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter divided_event_id = divide(event_id, 2)
| fields event_id, divided_event_id 
| limit 3 

Explanation: This query calculates a new field, divided_event_id, by dividing each event_id (an integer) by 2.

Output:

event_id divided_event_id
101 50.5
102 51.0
103 51.5

Example 2: Dividing a floating-point field by an integer literal

Goal: Calculate a new field by dividing a floating-point field (duration_seconds) by an integer literal.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter divided_duration = divide(duration_seconds, 5)
| fields event_id, duration_seconds, divided_duration 
| limit 3 

Explanation: This query divides duration_seconds (which contains decimal values) by 5, producing a new floating-point value in divided_duration.

Output:

event_id duration_seconds divided_duration
101 1.5 0.3
102 0.8 0.16
103 10.2 2.04

Example 3: Dividing a floating-point field by a floating-point literal

Goal: Divide a floating-point number field by a decimal literal.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter precise_division = divide(duration_seconds, 0.25)
| fields event_id, duration_seconds, precise_division 
| limit 3 

Explanation: This query divides duration_seconds by 0.25, resulting in precise_division which will also be a floating-point number.

Output:

event_id duration_seconds precise_division
101 1.5 6.0
102 0.8 3.2
103 10.2 40.8

Example 4: Dividing a numeric literal by a negative literal

Goal: Perform division using a negative literal value.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter negative_literal_division = divide(100, -4)
| fields event_id, negative_literal_division 
| limit 3 

Explanation: This query divides the positive literal 100 by the negative literal -4, producing a constant -25.0 for the negative_literal_division field across all records.

Output:

event_id negative_literal_division
101 -25.0
102 -25.0
103 -25.0

Example 5: Dividing a string-converted-to-number field

Goal: Divide a numeric value that was extracted as a string from a JSON field and converted to a number.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter simple_json_data != null 
| alter json_code_string = simple_json_data -> code 
| alter json_code_number = to_number(json_code_string) 
| alter divided_json_code = divide(json_code_number, 100) 
| fields event_id, simple_json_data, json_code_number, divided_json_code 
| limit 3 

Explanation: The query extracts the code "200" as a string, converts it to a number, and then divides it by 100, resulting in divided_json_code of 2.0. Records where the field is null will result in NULL.

Output:

event_id simple_json_data json_code_number divided_json_code
101 {"status": "ok", "code": 200} 200 2.0
102 {"status": "fail", "error": "access_denied"} NULL NULL
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL NULL

earliest

Use the earliest() function to retrieve the chronologically oldest value of a specified field within a grouped set of records.

Syntax

earliest(<field>)

Parameters

Name Type Required Description
field any Yes The field from which to extract the earliest value based on chronological order.

Returns

The earliest() function returns the value of the specified field from the oldest event in the evaluated group. The data type of the returned value matches the data type of the input field.

Usage Notes

  • The earliest() function is an aggregate function and must be used within the comp stage.
  • The chronological order of the records is determined by the underlying time field (such as _time) associated with the data events.
  • If the earliest record contains a NULL value for the evaluated field, the function will return NULL.
  • This function pairs frequently with the comp stage's by clause to find the first occurrence of an activity (like a process execution or login) per user or host.

Examples

Example 1: Identify the First Process Executed per Host

Goal: Find the earliest recorded process name that was executed on each endpoint within the queried time frame.

XQL Code:

config timeframe \= 1d  
| dataset \= xdr_data  
| filter action_process_image_name \!= null  
| comp earliest(action_process_image_name) as first_process by agent_hostname  
| fields agent_hostname, first_process  
| limit 3

Explanation: You use the comp stage to group the events by agent_hostname. For each unique host, the earliest() function evaluates the timeline of the records and returns the action_process_image_name associated with the chronologically oldest event in that group. The result is assigned to the first_process alias.

Output:

AGENT_HOSTNAME FIRST_PROCESS
endpoint-win-01 C:\Windows\System32\smss.exe
endpoint-mac-02 /sbin/launchd
srv-linux-03 /usr/lib/systemd/systemd

euclidean_distance

Use the euclidean_distance() function to calculate the Euclidean distance between two numeric vectors (arrays). The Euclidean distance is the straight-line distance between two points in multi-dimensional space.

Syntax

euclidean_distance(<vector1>, <vector2>)

Parameters

Name Type Required Description
vector1 array of integers or floats Yes The first numeric vector (array). Must have the same length as vector2.
vector2 array of integers or floats Yes The second numeric vector (array). Must have the same length as vector1.

Returns

Type: float

Description: The euclidean_distance() function returns a non-negative float representing the straight-line distance between the two input vectors. A value of 0.0 indicates identical vectors. If either input is null or the vectors have different lengths, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Vector Length: Both input vectors must have the same number of elements. If they differ in length, the function returns null.
  • Vector Type: Only float values are supported for the input vectors.
  • Null Handling: If either input expression is null, the function returns null.
  • Non-Negative Result: The result is always ≥ 0. A result of 0.0 means the two vectors are identical.
  • Formula: The Euclidean distance is calculated as sqrt(sum((v1[i] - v2[i])^2)) for all elements i.
  • Common Use Cases: This function is typically used within the alter stage for similarity analysis, anomaly detection, clustering, and comparing feature vectors in machine learning workflows.

Examples

Example 1: Calculate Euclidean distance between literal vectors

Goal: Calculate the Euclidean distance between specific vector pairs to verify known results.

XQL code:

dataset = xdr_data
| limit 1
| alter dist1 = euclidean_distance(arraycreate(0.0, 0.0), arraycreate(3.0, 4.0))
| alter dist2 = euclidean_distance(arraycreate(1.0, 2.0, 3.0), arraycreate(1.0, 2.0, 3.0))
| fields dist1, dist2

Explanation: The distance between (0.0,0.0) and (3.0,4.0) is 5.0 (a classic 3-4-5 right triangle). The distance between two identical vectors (1.0,2.0,3.0) is 0.0.

Output:

DIST1 DIST2
5.0 0.0

Example 2: Calculate Euclidean distance between field vectors

Goal: Compute the Euclidean distance between an array field and a reference vector.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_codes != null and array_length(numeric_codes) = 5
| alter reference_vector = arraycreate(10.0, 20.0, 30.0, 40.0, 50.0)
| alter euc_dist = euclidean_distance(numeric_codes, reference_vector)
| fields event_id, numeric_codes, euc_dist
| limit 3

Explanation: This query creates a reference vector and computes the Euclidean distance between each record's numeric_codes array and the reference vector. Lower values indicate the vectors are closer together in multi-dimensional space.

Output:

EVENT_ID NUMERIC_CODES EUC_DIST
101 [13, -47, 29, 82, -15] 107.35
102 [-21, 56, 13, -88, 42] 148.92
105 [8, 15, 25, 35, 45] 9.95

Example 3: Find most similar records using Euclidean distance

Goal: Sort records by their Euclidean distance to a reference vector to find the most similar ones.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_codes != null and array_length(numeric_codes) = 5
| alter ref = arraycreate(10.0, 20.0, 30.0, 40.0, 50.0)
| alter distance = euclidean_distance(numeric_codes, ref)
| fields event_id, numeric_codes, distance
| sort asc distance
| limit 3

Explanation: This query computes the Euclidean distance between each record's numeric_codes and a reference vector, then sorts by distance ascending to show the most similar records first.

Output:

EVENT_ID NUMERIC_CODES DISTANCE
105 [8, 15, 25, 35, 45] 9.95
101 [13, -47, 29, 82, -15] 107.35
102 [-21, 56, 13, -88, 42] 148.92

exp

Use the exp() function to calculate the value of Euler's number e raised to the power of a numeric value.

Syntax

exp(<number>)

Parameters

Name Type Required Description
number integer, float Yes The exponent to which e (approximately 2.71828) is raised. Any real number is valid.

Returns

Type: float

Description: The exp() function returns e raised to the power of the input value (e^x). If the input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The exp() function accepts any real number as input.
  • Null Handling: If the input expression is null, the function returns null.
  • Identity Value: exp(0) always returns 1.0, since e^0 = 1.
  • Inverse Relationship: The exp() function is the inverse of the natural logarithm ln(). That is, exp(ln(x)) = x for x > 0.
  • Overflow: For very large positive input values, the result may overflow to infinity.
  • Underflow: For very large negative input values, the result approaches 0.0.
  • Common Use Cases: This function is typically used within the alter stage for exponential growth/decay modeling, probability calculations, and scientific computations.

Examples

Example 1: Calculate exponential of literal values

Goal: Calculate e raised to specific powers to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = exp(0), result2 = exp(1), result3 = exp(-1)
| fields result1, result2, result3

Explanation: You use exp() on three different literal values. exp(0) returns 1.0, exp(1) returns approximately 2.71828 (the value of e), and exp(-1) returns approximately 0.36788 (1/e).

Output:

RESULT1 RESULT2 RESULT3
1.0 2.71828 0.36788

Example 2: Calculate exponential from a field value

Goal: Calculate the exponential of values stored in a dataset field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter exp_result = exp(duration_seconds)
| fields event_id, duration_seconds, exp_result
| limit 3

Explanation: This query computes e raised to the power of each value in the duration_seconds field using exp() in the alter stage. No filtering is needed since exp() accepts any real number.

Output:

EVENT_ID DURATION_SECONDS EXP_RESULT
101 0.0 1.0
102 1.0 2.71828
103 2.0 7.38906

Example 3: Verify inverse relationship with ln

Goal: Demonstrate that exp() and ln() are inverse functions by applying both sequentially.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value > 0
| alter ln_val = ln(numeric_value)
| alter exp_ln_val = exp(ln_val)
| fields event_id, numeric_value, ln_val, exp_ln_val
| limit 3

Explanation: This query first computes the natural logarithm of numeric_value, then applies exp() to the result. The final value exp_ln_val should match the original numeric_value, confirming the inverse relationship exp(ln(x)) = x.

Output:

EVENT_ID NUMERIC_VALUE LN_VAL EXP_LN_VAL
101 5.0 1.60944 5.0
102 10.0 2.30259 10.0
103 100.0 4.60517 100.0

extract_time

Use the extract_time() function to isolate and return specific components of a timestamp, such as the year, month, or hour.

Important Note: The values returned by extract_time() are always based on GMT (Greenwich Mean Time), even if your server settings for Timezone or Timestamp Format are adjusted.

Syntax

extract_time (<timestamp>, <part>)

Parameters

Name Type Required Description
timestamp timestamp Yes The input timestamp value, originating from a field or the result of another function.
part string Yes The specific unit of the timestamp to extract.

Returns

The extract_time() function returns a numerical value (Integer or Number) representing the extracted portion of the timestamp.

Usage notes

  • The values returned by extract_time() are always based on GMT (Greenwich Mean Time) even if you've adjusted the Timezone or Timestamp Format server settings. For more information on the server settings, see Configure server settings.
  • The part parameter is not case-sensitive, but the string must match one of the supported keywords.
  • The supported keywords for the part parameter are:
    • DAY: Day of the month (1-31).
    • DAYOFWEEK: Day of the week (1=Monday, 7=Sunday).
    • DAYOFYEAR: Day of the year (1-366).
    • HOUR: Hour of the day (0-23).
    • MICROSECOND: Microseconds.
    • MILLISECOND: Milliseconds.
    • MINUTE: Minute of the hour (0-59).
    • MONTH: Month of the year (1-12).
    • QUARTER: Quarter of the year (1-4).
    • SECOND: Second of the minute (0-59).
    • YEAR: Year (for example, 2023).

Examples

Example 1: Extracting the YEAR

Goal: Extract the year from the _time field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_year = extract_time(_time, "YEAR") 
| fields event_id, _time, event_year 
| limit 3 

Explanation: The event_year field shows the integer year (2023) extracted from each event's _time timestamp.

Output:

EVENT_ID _TIME EVENT_YEAR
101 Oct 26th 2023 10:00:00 2023
102 Oct 26th 2023 10:05:30 2023
103 Oct 26th 2023 10:15:15 2023

Example 2: Extracting the MONTH

Goal: Extract the month from the _time field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_month = extract_time(_time, "MONTH") 
| fields event_id, _time, event_month 
| limit 3 

Explanation: The event_month field shows the integer month (10 for October) extracted from each event's _time timestamp.

Output:

EVENT_ID _TIME EVENT_MONTH
101 Oct 26th 2023 10:00:00 10
102 Oct 26th 2023 10:05:30 10
103 Oct 26th 2023 10:15:15 10

Example 3: Extracting the DAY

Goal: Extract the day of the month from the _time field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_day = extract_time(_time, "DAY") 
| fields event_id, _time, event_day 
| limit 3 

Explanation: The event_day field shows the integer day of the month (26) extracted from each event's _time timestamp.

Output:

EVENT_ID _TIME EVENT_DAY
101 Oct 26th 2023 10:00:00 26
102 Oct 26th 2023 10:05:30 26
103 Oct 26th 2023 10:15:15 26

Example 4: Extracting the DAYOFWEEK

Goal: Extract the day of the week from the _time field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_day_of_week = extract_time(_time, "DAYOFWEEK") 
| fields event_id, _time, event_day_of_week 
| limit 3 

Explanation: The event_day_of_week field shows the integer day of the week (4 for Thursday) extracted from each event's _time timestamp.

Output:

EVENT_ID _TIME EVENT_DAY_OF_WEEK
101 Oct 26th 2023 10:00:00 4
102 Oct 26th 2023 10:05:30 4
103 Oct 26th 2023 10:15:15 4

Example 5: Extracting the DAYOFYEAR

Goal: Extract the day of the year from the _time field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_day_of_year = extract_time(_time, "DAYOFYEAR") 
| fields event_id, _time, event_day_of_year 
| limit 3 

Explanation: The event_day_of_year field shows the integer day of the year (299) extracted from each event's _time timestamp.

Output:

EVENT_ID _TIME EVENT_DAY_OF_YEAR
101 Oct 26th 2023 10:00:00 299
102 Oct 26th 2023 10:05:30 299
103 Oct 26th 2023 10:15:15 299

Example 6: Extracting the HOUR

Goal: Extract the hour from the _time field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_hour = extract_time(_time, "HOUR") 
| fields event_id, _time, event_hour 
| limit 3 

Explanation: The event_hour field shows the integer hour (10) extracted from each event's _time timestamp.

Output:

EVENT_ID _TIME EVENT_HOUR
101 Oct 26th 2023 10:00:00 10
102 Oct 26th 2023 10:05:30 10
103 Oct 26th 2023 10:15:15 10

Example 7: Extracting the MINUTE

Goal: Extract the minute from the _time field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_minute = extract_time(_time, "MINUTE") 
| fields event_id, _time, event_minute 
| limit 3 

Explanation: The event_minute field shows the integer minute extracted from each event's _time timestamp.

Output:

EVENT_ID _TIME EVENT_MINUTE
101 Oct 26th 2023 10:00:00 0
102 Oct 26th 2023 10:05:30 5
103 Oct 26th 2023 10:15:15 15

Example 8: Extracting the SECOND

Goal: Extract the second from the _time field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_second = extract_time(_time, "SECOND") 
| fields event_id, _time, event_second 
| limit 3 

Explanation: The event_second field shows the integer second extracted from each event's _time timestamp.

Output:

EVENT_ID _TIME EVENT_SECOND
101 Oct 26th 2023 10:00:00 0
102 Oct 26th 2023 10:05:30 30
103 Oct 26th 2023 10:15:15 15

Example 9: Extracting the MILLISECOND

Goal: Extract the milliseconds from a timestamp. Because the sample data does not explicitly display milliseconds, this example uses current_time() for demonstration.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter current_ts_val = current_time() // Conceptual: Jul 25th 2024 14:30:45.123 
| alter ms_part = extract_time(current_ts_val, "MILLISECOND") 
| fields event_id, current_ts_val, ms_part 
| limit 3 

Explanation: The ms_part field shows the millisecond component (123) extracted from the current time. This illustrates the function's capability with higher precision timestamps.

Output:

EVENT_ID CURRENT_TS_VAL MS_PART
101 Jul 25th 2024 14:30:45 123
102 Jul 25th 2024 14:30:45 123
103 Jul 25th 2024 14:30:45 123

Example 10: Extracting the MICROSECOND

Goal: Extract the microseconds from a timestamp. Because the sample data does not explicitly display microseconds, this example uses current_time() for demonstration.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter current_ts_val = current_time() // Conceptual: Jul 25th 2024 14:30:45.123456 
| alter us_part = extract_time(current_ts_val, "MICROSECOND") 
| fields event_id, current_ts_val, us_part 
| limit 3 

Explanation: The us_part field shows the microsecond component (123456) extracted from the current time, demonstrating the function's precision.

Output:

EVENT_ID CURRENT_TS_VAL US_PART
101 Jul 25th 2024 14:30:45 123456
102 Jul 25th 2024 14:30:45 123456
103 Jul 25th 2024 14:30:45 123456

Example 11: Extracting the QUARTER

Goal: Extract the quarter of the year from the _time field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_quarter = extract_time(_time, "QUARTER") 
| fields event_id, _time, event_quarter 
| limit 3 

Explanation: The event_quarter field shows the integer quarter of the year (4) extracted from each event's _time timestamp.

Output:

EVENT_ID _TIME EVENT_QUARTER
101 Oct 26th 2023 10:00:00 4
102 Oct 26th 2023 10:05:30 4
103 Oct 26th 2023 10:15:15 4

extract_url_host

Use the extract_url_host() function to return the host portion of a given URL string.

Syntax

extract_url_host (<url_string>)

Parameters

Name Type Required Description
url_string string Yes The input string literal or field containing the URL from which the host needs to be extracted.

Returns

The extract_url_host() function returns a string representing the host of the URL.

Usage notes

  • The function always returns the host value in lowercase characters, regardless of the input URL's original casing.
  • This function is typically used within the alter stage to create new fields or modify existing ones based on extracted URL data, or within the filter stage for conditional logic.
  • If the input url_string is NULL, the function returns NULL.

Examples

Example 1: Basic host extraction from a standard URL

Goal: Extract the host from a common HTTPS URL.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_host = extract_url_host("https://www.paloaltonetworks.com") 
| fields event_id, extracted_host 
| limit 1 

Explanation: The query extracts the host "www.paloaltonetworks.com" from the provided URL string.

Output:

EVENT_ID EXTRACTED_HOST
101 www.paloaltonetworks.com

Example 2: Host extraction from a URL with user, password, and port

Goal: Extract the host from a complex URL that includes user credentials and port numbers.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_host_complex = extract_url_host("//user:password@a.b:80/path?query") 
| fields event_id, extracted_host_complex 
| limit 1 

Explanation: extract_url_host() successfully isolates "a.b" as the host, ignoring the user, password, port, path, and query parameters.

Output:

EVENT_ID EXTRACTED_HOST_COMPLEX
101 a.b

Example 3: Host extraction with mixed-case input

Goal: Extract the host from a URL containing uppercase letters, demonstrating the automatic lowercasing behavior.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_host_lowercase = extract_url_host("www.Example.Co.UK") 
| fields event_id, extracted_host_lowercase 
| limit 1 

Explanation: Despite "www.Example.Co.UK" having mixed casing, the function returns "www.example.co.uk" in all lowercase, demonstrating its built-in lowercasing behavior.

Output:

EVENT_ID EXTRACTED_HOST_LOWERCASE
101 <www.example.co.uk>

Example 4: Host extraction from a URL with multiple suffixes/paths

Goal: Extract the full hostname from a URL that includes subdomains and extended paths.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_host_path = extract_url_host("https://www.test.paloaltonetworks.com/suffix/another_suffix") 
| fields event_id, extracted_host_path 
| limit 1 

Explanation: The function correctly identifies the entire host, including subdomains, regardless of the trailing path components.

Output:

EVENT_ID EXTRACTED_HOST_PATH
101 www.test.paloaltonetworks.com

Example 5: Handling NULL input

Goal: Demonstrate the function's behavior when provided with a NULL input.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter null_url_input = NULL 
| alter extracted_host_from_null = extract_url_host(null_url_input) 
| fields event_id, null_url_input, extracted_host_from_null 
| limit 1 

Explanation: Consistent with standard XQL function behavior, if the input to extract_url_host() is NULL, the function returns NULL.

Output:

EVENT_ID NULL_URL_INPUT EXTRACTED_HOST_FROM_NULL
101 NULL NULL

Example: Host extraction from a URL string

Goal: Return a single record from the sample_xql_raw dataset showing the extracted host name from a specific URL string ("https://www.test.paloaltonetworks.com").

XQL Code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter url_host = extract_url_host("https://www.test.paloaltonetworks.com") 
| fields url_host 
| limit 1

Explanation:

  1. The query starts by accessing the xdr_data dataset.
  2. The alter stage creates a new field named url_host.
  3. The extract_url_host() function parses the provided URL string and isolates the host component (www.test.paloaltonetworks.com).
  4. The fields stage restricts the final output to only show the newly created url_host column.
  5. The limit 1 stage ensures that only a single result is returned.

Output:

url_host
"www.test.paloaltonetworks.com"

extract_url_pub_suffix

Use the extract_url_pub_suffix() function to return the public suffix of a given URL string (for example, "com", "org", "net").

Syntax

extract_url_pub_suffix ("<URL>")

Parameters

Name Type Required Description
<URL> string Yes The input string literal or field containing the URL from which the public suffix needs to be extracted.

Returns

The extract_url_pub_suffix() function returns a string representing the public suffix of the URL.

Usage notes

  • The function requires a string value representing a URL as input.
  • The function always returns the public suffix value in lowercase characters, regardless of the input URL's original casing.
  • This function is typically used within the alter stage to create new fields or modify existing ones based on extracted URL data, or within the filter stage for conditional logic.

Examples

Example 1: Basic public suffix extraction from a standard URL

Goal: Extract the public suffix from a common HTTPS URL.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_pub_suffix = extract_url_pub_suffix("https://www.paloaltonetworks.com") 
| fields event_id, extracted_pub_suffix 
| limit 1 

Explanation: The query extracts "com" as the public suffix from the URL "https://www.paloaltonetworks.com".

Output:

EVENT_ID EXTRACTED_PUB_SUFFIX
101 "com"

Example 2: Public suffix extraction from a URL with multiple suffixes/paths

Goal: correctly identify only the public suffix from a URL that includes subdomains and extended paths.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_pub_suffix_complex = extract_url_pub_suffix("https://www.test.paloaltonetworks.com/suffix/another_suffix") 
| fields event_id, extracted_pub_suffix_complex 
| limit 1 

Explanation: The function correctly isolates "com" as the public suffix, ignoring subdomains and path components.

Output:

EVENT_ID EXTRACTED_PUB_SUFFIX_COMPLEX
101 "com"

Example 3: Public suffix extraction with mixed-case input

Goal: Demonstrate that the function returns the public suffix in lowercase characters, even if the input URL contains uppercase letters.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_pub_suffix_lowercase = extract_url_pub_suffix("www.Example.Co.UK") 
| fields event_id, extracted_pub_suffix_lowercase 
| limit 1 

Explanation: Despite "www.Example.Co.UK" having mixed casing, the function returns "uk" in all lowercase, demonstrating its built-in lowercasing behavior.

Output:

EVENT_ID EXTRACTED_PUB_SUFFIX_LOWERCASE
101 "uk"

Example 4: Handling NULL input

Goal: Demonstrate the function's behavior when provided with a NULL input.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter null_url_input = NULL 
| alter extracted_pub_suffix_from_null = extract_url_pub_suffix(null_url_input) 
| fields event_id, null_url_input, extracted_pub_suffix_from_null 
| limit 1 

Explanation: Consistent with standard XQL function behavior, if the input to extract_url_pub_suffix() is NULL, the function returns NULL.

Output:

EVENT_ID NULL_URL_INPUT EXTRACTED_PUB_SUFFIX_FROM_NULL
101 NULL NULL

extract_url_registered_domain

Use the extract_url_registered_domain() function to retrieve the registered domain (also known as the registerable domain) from a URL string. This typically consists of the public suffix plus one preceding label (for example, extracting paloaltonetworks.com from www.paloaltonetworks.com).

Syntax

extract_url_registered_domain ("<URL>")

Parameters

Name Type Required Description
<URL> string Yes The input string literal or field containing the URL from which the registered domain is extracted.

Returns

The extract_url_registered_domain() function returns a string representing the registered domain of the URL.

Usage notes

  • The function accepts a string value representing a URL as input.
  • The function always returns the registered domain value in lowercase characters, regardless of the casing in the input URL.
  • If the input URL structure does not contain a recognizable registered domain (such as a private IP address or a malformed URL), the function returns NULL.
  • If the input value is NULL, the function returns NULL.

Examples

Example 1: Basic registered domain extraction from a standard URL

Goal: Extract the registered domain from a standard HTTPS URL.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter extracted_registered_domain = extract_url_registered_domain("https://www.paloaltonetworks.com")
| fields event_id, extracted_registered_domain
| limit 1

Explanation: The query extracts "paloaltonetworks.com" as the registered domain from the URL "https://www.paloaltonetworks.com".

Output:

EVENT_ID EXTRACTED_REGISTERED_DOMAIN
101 paloaltonetworks.com

Example 2: Registered domain extraction from a URL with multiple subdomains/paths

Goal: Extract the registered domain from a URL that includes multiple subdomains and extended path components.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter extracted_registered_domain_complex = extract_url_registered_domain("https://docs.global.paloaltonetworks.com/xql/api")
| fields event_id, extracted_registered_domain_complex
| limit 1

Explanation: The function correctly isolates "paloaltonetworks.com" as the registered domain, ignoring the "docs.global" subdomains and the "/xql/api" path components.

Output:

EVENT_ID EXTRACTED_REGISTERED_DOMAIN_COMPLEX
101 paloaltonetworks.com

Example 3: Registered domain extraction with mixed-case input

Goal: Extract the registered domain from a URL containing uppercase letters to demonstrate automatic lowercasing.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter extracted_registered_domain_lowercase = extract_url_registered_domain("WWW.EXAMPLE.Co.UK/docs")
| fields event_id, extracted_registered_domain_lowercase
| limit 1

Explanation: Despite the input "WWW.EXAMPLE.Co.UK/docs" having mixed casing, the function returns the registered domain "example.co.uk" in all lowercase characters.

Output:

EVENT_ID EXTRACTED_REGISTERED_DOMAIN_LOWERCASE
101 example.co.uk

Example 4: Handling URLs without a recognizable registered domain

Goal: Demonstrate the function's behavior when provided with inputs that do not represent a recognizable registered domain, such as malformed URLs or private IP addresses.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter invalid_url_registered_domain = extract_url_registered_domain("//user:password@a.b:80/path?query")
| alter private_ip_registered_domain = extract_url_registered_domain("192.168.1.10")
| fields event_id, invalid_url_registered_domain, private_ip_registered_domain
| limit 1

Explanation: For the malformed URL and the private IP address "192.168.1.10", the function cannot determine a registered domain and returns NULL.

Output:

EVENT_ID INVALID_URL_REGISTERED_DOMAIN PRIVATE_IP_REGISTERED_DOMAIN
101 NULL NULL

Example 5: Handling NULL input

Goal: Demonstrate the behavior when the input field is NULL.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id = 105
| alter extracted_registered_domain_from_null = extract_url_registered_domain(dst_domain)
| fields event_id, dst_domain, extracted_registered_domain_from_null
| limit 1

Explanation: The dst_domain field is NULL for event 105. Consistent with standard XQL function behavior, the function returns NULL.

Output:

EVENT_ID DST_DOMAIN EXTRACTED_REGISTERED_DOMAIN_FROM_NULL
105 NULL NULL

first

Use the first() function to retrieve the first chronologically recorded value of a specified field within a grouped set of records.

Syntax

first(<field>)

Parameters

Name Type Required Description
field any Yes The field from which to extract the first recorded value based on chronological order.

Returns

The first() function returns the value of the specified field from the oldest event in the evaluated group. The data type of the returned value matches the data type of the input field.

Usage Notes

  • The first() function is an aggregate function and must be used within the comp stage.
  • The chronological order of the records is determined by the underlying time field (such as _time) associated with the data events.
  • If the first record contains a NULL value for the evaluated field, the function will return NULL.
  • This function is frequently used in threat hunting to identify the initial occurrence of an artifact or behavior, such as the first command executed in a session or the initial process started by a specific user.

Examples

Example 1: Identify the First Process Executed per Host

Goal: Find the first recorded process name that was executed on each endpoint within the queried time frame.

XQL Code:

config timeframe = 1d  
| dataset = xdr_data  
| filter action_process_image_name != null  
| comp first(action_process_image_name) as first_process by agent_hostname  
| fields agent_hostname, first_process  
| limit 3

Explanation: You use the comp stage to group the events by agent_hostname. For each unique host, the first() function evaluates the timeline of the records and returns the action_process_image_name associated with the chronologically oldest event in that group. The result is assigned to the first_process alias.

Output:

AGENT_HOSTNAME FIRST_PROCESS
endpoint-win-01 C:\Windows\System32\smss.exe
endpoint-mac-02 /sbin/launchd
srv-linux-03 /usr/lib/systemd/systemd

Example 2: Retrieve the First Command Line for a Process

Goal: Identify the first command line argument used to execute a specific process across your environment.

XQL Code:

config timeframe = 1d  
| dataset = xdr_data  
| filter action_process_image_name = "powershell.exe"  
| comp first(action_process_image_command_line) as initial_cmd by agent_hostname  
| fields agent_hostname, initial_cmd  
| limit 2

Explanation: This query filters for execution events involving powershell.exe. Using the comp stage, it groups the results by agent_hostname and uses the first() function to extract the chronologically first command line argument executed by PowerShell on that host.

Output:

AGENT_HOSTNAME INITIAL_CMD
endpoint-win-01 powershell.exe -ExecutionPolicy Bypass -File C:\scripts\setup.ps1
srv-win-02 "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile

first_value

Use the first_value() function to retrieve the first value of a specified field within an ordered partition of records, typically used for baseline comparisons across a sequence of events.

Syntax

first_value(<field>)

Parameters

Name Type Required Description
field any Yes The field from which to extract the first recorded value based on the window's defined ordering.

Returns

The first_value() function returns the value of the specified field from the first row of the evaluated window frame. The data type of the returned value matches the data type of the input field.

Usage Notes

  • The first_value() function is a window function and must be used strictly within the windowcomp stage.
  • The chronological or logical order of the records is determined by the order by clause within the over() partition statement.
  • Unlike the first() aggregate function used in the comp stage, first_value() appends the retrieved baseline value to every row in the partition without collapsing the dataset, enabling row-by-row comparative analysis.
  • If the evaluated field for the first row in the window is NULL, the function will return NULL for the records in that window frame.

Examples

Example 1: Establish an Initial Process Baseline per Host**

Goal: Identify the first recorded process name executed on each endpoint and append this baseline value alongside all subsequent process executions to spot deviations.

XQL Code:

config timeframe = 1d  
| dataset = xdr_data  
| filter action_process_image_name != null  
| windowcomp first_value(action_process_image_name) as initial_process over(partition by agent_hostname order by _time asc)  
| fields _time, agent_hostname, action_process_image_name, initial_process  
| limit 3

Explanation: You use the windowcomp stage to partition the events by agent_hostname and order them chronologically using _time asc. The first_value() function evaluates this ordered window and captures the first action_process_image_name encountered in each partition. This value is assigned to the initial_process alias and appended to every row for that host, allowing you to easily compare the current process (action_process_image_name) against the first process (initial_process).

Output:

_TIME AGENT_HOSTNAME ACTION_PROCESS_IMAGE_NAME INITIAL_PROCESS
2023-10-26 10:00:00 UTC endpoint-win-01 C:\Windows\System32\smss.exe C:\Windows\System32\smss.exe
2023-10-26 10:05:30 UTC endpoint-win-01 C:\Windows\System32\cmd.exe C:\Windows\System32\smss.exe
2023-10-26 10:20:00 UTC endpoint-win-01 C:\Windows\System32\powershell.exe C:\Windows\System32\smss.exe

Example 2: Return the first value of a field

Goal: Extract the original remote port in a network connection.

XQL Code:

dataset = xdr_data
| filter event_type = ENUM.NETWORK
| filter action_remote_port != null  
| windowcomp first_value(action_remote_port) as first_remote_port over(partition by agent_hostname order by _time asc)
| fields agent_hostname, action_remote_port, first_remote_port, _time
| sort asc agent_hostname , _time

Explanation: The windowcomp partition operates within the parameters defined in the over() partition statement. The query organizes the data to evaluate by agent_hostname according to the ascending chronological order of its creation time. The results returned in the new field first_remote_port provide the remote port value of the earliest creation time event for each agent hostname. Because this calculation utilizes the windowcomp stage, all lines of the dataset are returned along with this new field value.

Output:

AGENT_HOSTNAME ACTION_REMOTE_PORT FIRST_REMOTE_PORT _TIME
mac-agent-1 443 443 Mar 26th 2026 09:26:07
mac-agent-1 443 443 Mar 26th 2026 09:26:08
mac-agent-1 80 443 Mar 26th 2026 09:26:09

floor

Use the floor() function to convert a field containing a number and return an integer rounded down to the nearest whole number.

Syntax

floor (<number>)

Parameters

Name Type Required Description
number integer, float Yes The numeric value to round down.

Returns

The floor() function returns an integer representing the input number rounded down to the nearest whole number.

Usage notes

  • The function accepts a field that contains a number, meaning it can take integers or floating-point numbers as input.
  • The function explicitly rounds the number down to the nearest whole integer.
  • For positive numbers, this behavior truncates the decimal part (for example, 1.9 becomes 1).
  • For negative numbers, it rounds to the next more negative integer (for example, -2.5 becomes -3).

Examples

Example 1: Round down positive floating-point field

Goal: Round the duration_seconds field down to the nearest integer.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter floored_duration = floor(duration_seconds) 
| fields event_id, duration_seconds, floored_duration 
| limit 3 

Explanation: This query creates a new field, floored_duration, by taking the duration_seconds (for example, 1.5, 0.8) and rounding it down. For 1.5, it becomes 1; for 0.8, it becomes 0.

Output:

EVENT_ID DURATION_SECONDS FLOORED_DURATION
101 1.5 1
102 0.8 0
103 10.2 10

Example 2: Round down calculated floating-point value

Goal: Round the result of a division operation on an integer field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter divided_event_id = divide(event_id, 3) 
| alter floored_divided_id = floor(divided_event_id) 
| fields event_id, divided_event_id, floored_divided_id 
| limit 3 

Explanation: Here, event_id (for example, 101) is divided by 3, resulting in 33.666.... The floor() function then rounds this down to 33.

Output:

EVENT_ID DIVIDED_EVENT_ID FLOORED_DIVIDED_ID
101 33.666666 33
102 34.0 34
103 34.333333 34

Example 3: Round down negative number

Goal: Round a negative number extracted from an array field to demonstrate rounding towards negative infinity.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_numeric_code = arrayindex(numeric_codes, 1) 
| alter floored_negative_code = floor(first_numeric_code) 
| fields event_id, numeric_codes, first_numeric_code, floored_negative_code 
| limit 3 

Explanation: For event_id 101, first_numeric_code is -47. The floor() of -47 is still -47 as it's an integer. If a negative float like -2.5 were present, floor() would convert it to -3.

Output:

EVENT_ID NUMERIC_CODES FIRST_NUMERIC_CODE FLOORED_NEGATIVE_CODE
101 [13, -47, 29, 82, -15] -47 -47
102 [-21, 56, 13, -88, 42] 56 56
103 [90, -33, 7, 51, -62, 18] -33 -33

Example 4: Round down number extracted from JSON

Goal: Round a numeric value extracted from a JSON field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 110 
| alter json_size_string = simple_json_data -> size_gb 
| alter json_size_number = to_number(json_size_string) 
| alter floored_json_size = floor(json_size_number) 
| fields event_id, simple_json_data, json_size_number, floored_json_size 
| limit 1 

Explanation: This query extracts the size_gb value from the simple_json_data field (for example, "500" for event_id 110). The query then converts this string to a number and applies floor(), which for an integer like 500 would result in 500.

Output:

EVENT_ID SIMPLE_JSON_DATA JSON_SIZE_NUMBER FLOORED_JSON_SIZE
110 {"backup_id": "DB-005", "size_gb": 500} 500 500

format_string

Use the format_string() function to construct dynamic strings by inserting values into a predefined format.

Syntax

format_string ("<format_string>", <field_1>, <field_2>, ...<field_n>)

Parameters

Name Type Required Description
format_string string Yes A string literal that contains zero or more format specifiers initiated by the % symbol (for example, %s, %d).
field_n string, integer Yes A variable-length list of additional arguments whose values will be inserted into the format string. Each argument must match the type expected by its corresponding format specifier.

Returns

The format_string() function returns a single string containing the formatted text with argument values inserted.

Usage notes

  • Common format specifiers include %s for string values and %d for integer values.
  • You can use padding and width specifiers (for example, %10d, %05d, %-10s) to control output alignment and formatting.
  • The function requires strict type compatibility between arguments and specifiers. The function does not implicitly convert types (for example, a floating-point number passed to %d will fail).
  • Use explicit type conversion functions like to_string() or to_integer() to ensure non-matching values (like booleans or floats) are compatible with the specifiers.
  • Note that to_number() converts strings to floating-point numbers, which are incompatible with the %d (integer) specifier; use to_integer() instead.

Examples

Example 1: Basic string substitution (%s)

Goal: Combine literal text with string field values using the %s specifier.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_message = format_string("Description: %s. Domain: %s.", event_description, dst_domain)
| fields event_id, event_message
| limit 3

Explanation: The event_description and dst_domain fields, which are strings, are directly inserted into the format string at the %s placeholders.

Output:

EVENT_ID EVENT_MESSAGE
101 "Description: User login successful. Domain: ec2.amazonaws.com."
102 "Description: File access attempt. Domain: sts.amazonaws.com."
103 "Description: Network connection established. Domain: www.google.com."

Example 2: Integer substitution (%d)

Goal: Embed integer field values into a string using the %d specifier.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter formatted_id = format_string("Event #%d occurred.", event_id)
| fields event_id, formatted_id
| limit 3

Explanation: The event_id (an integer) is converted to its string representation and inserted into the output string where %d is specified.

Output:

EVENT_ID FORMATTED_ID
101 "Event #101 occurred."
102 "Event #102 occurred."
103 "Event #103 occurred."

Example 3: Combining different data types

Goal: Combine values of different original data types (boolean, float) into a single formatted string using explicit conversion.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter detailed_status = format_string("Successful: %s, Duration: %s seconds", to_string(is_successful), to_string(duration_seconds))
| fields event_id, is_successful, duration_seconds, detailed_status
| limit 3

Explanation: to_string(is_successful) converts the boolean to "true" or "false". to_string(duration_seconds) converts the float to a string like "1.5". format_string() then constructs the final string using these string representations for the %s specifiers.

Output:

EVENT_ID IS_SUCCESSFUL DURATION_SECONDS DETAILED_STATUS
101 true 1.5 "Successful: true, Duration: 1.5 seconds"
102 false 0.8 "Successful: false, Duration: 0.8 seconds"
103 true 10.2 "Successful: true, Duration: 10.2 seconds"

Example 4: Padding and zero-padding for integers

Goal: Utilize padding specifiers with %d and %s to control width and alignment.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter padded_event_id = format_string("ID: %05d | Full ID: %-10s | Raw: %s", event_id, to_string(event_id), to_string(event_id))
| fields event_id, padded_event_id
| limit 3

Explanation: %05d pads event_id with leading zeros to a width of 5. %-10s left-justifies the string version within 10 characters. %s performs basic substitution.

Output:

EVENT_ID PADDED_EVENT_ID
101 ID: 00101
102 ID: 00101
103 ID: 00101

Example 5: Formatting extracted JSON scalar values with integer conversion

Goal: Extract a numeric string from JSON, convert it to an integer, and format it with %d.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter status_code_string = coalesce(simple_json_data -> code, simple_json_data -> error_code)
| alter status_int = to_integer(status_code_string)
| alter formatted_status = format_string("API Status: %d", status_int)
| fields event_id, simple_json_data, formatted_status
| limit 3

Explanation: The query extracts the code as a string and explicitly converts it to an integer using to_integer(). This ensures compatibility with the %d specifier, which would fail if a float (from to_number) were passed.

Output:

EVENT_ID SIMPLE_JSON_DATA FORMATTED_STATUS
101 {"status": "ok", "code": 200} "API Status: 200"
102 {"status": "fail", "error": "access_denied"} NULL
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL

format_timestamp

Use the format_timestamp() function to return a string by formatting a given timestamp according to a specified string format.

Syntax

format_timestamp ("<format string>", <timestamp field> [, "<time zone>"])

Parameters

Name Type Required Description
format_string string Yes The string literal that defines how the timestamp will be formatted (for example, %Y/%m/%d %H:%M:%S).
timestamp_field timestamp Yes The timestamp value to be formatted.
time_zone string No An optional time zone specified using either an hours offset (for example, +08:00) or a time zone name (for example, America/Chicago). The default is UTC.

Returns

The format_timestamp() function returns a string representation of the timestamp.

Usage notes

  • The function requires a format string and a timestamp field as input.
  • An optional time zone can be specified to adjust the displayed time.
  • If no time zone is configured, the function defaults to UTC.
  • This function is typically used within the alter stage to create new fields containing formatted time strings.

Examples

Example 1: Without a time zone configured

Goal: Format the _time field into a specific string format using the default UTC time zone.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_time = format_timestamp("%Y/%m/%d %H:%M:%S", _time) 
| fields event_id, _time, new_time 
| limit 3 

Explanation: The _time field, which is in UTC, is formatted directly into a string. Since no time zone is specified in the format_timestamp() function, the output new_time string reflects the UTC time.

Output:

EVENT_ID _TIME NEW_TIME
101 Oct 26th 2023 10:00:00 2023/10/26 10:00:00
102 Oct 26th 2023 10:05:30 2023/10/26 10:05:30
103 Oct 26th 2023 10:15:15 2023/10/26 10:15:15

Example 2: With a time zone configured using an hours offset

Goal: Format the _time field into a string, adjusting the time by a specific hours offset.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_time = format_timestamp("%Y/%m/%d %H:%M:%S", _time, "+03:00") 
| fields event_id, _time, new_time 
| limit 3 

Explanation: The _time field (UTC) is adjusted by adding three hours (+03:00) before it is formatted into the new_time string. For example, 10:00:00 UTC becomes 13:00:00 in the formatted string.

Output:

EVENT_ID _TIME NEW_TIME
101 Oct 26th 2023 10:00:00 2023/10/26 13:00:00
102 Oct 26th 2023 10:05:30 2023/10/26 13:05:30
103 Oct 26th 2023 10:15:15 2023/10/26 13:15:15

Example 3: With a time zone name configured

Goal: Format the _time field into a string, applying a specific named time zone.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_time = format_timestamp("%Y/%m/%d %H:%M:%S", _time, "America/Chicago") 
| fields event_id, _time, new_time 
| limit 3 

Explanation: The _time field (UTC) is converted to the America/Chicago time zone (UTC-5 for this date) before being formatted. For example, 10:00:00 UTC becomes 05:00:00 in the formatted new_time string.

Output:

EVENT_ID _TIME NEW_TIME
101 Oct 26th 2023 10:00:00 2023/10/26 05:00:00
102 Oct 26th 2023 10:05:30 2023/10/26 05:05:30
103 Oct 26th 2023 10:15:15 2023/10/26 05:15:15

greatest

Use the greatest() function to return the largest value from a list of expressions.

Syntax

greatest(<expression1>, <expression2> [, ...])

Parameters

Name Type Required Description
expression1 integer, float, string, timestamp Yes The first value to compare.
expression2 integer, float, string, timestamp Yes The second value to compare.
... integer, float, string, timestamp No Additional values to compare. Any number of additional expressions can be provided.

Returns

Type: Same as input type

Description: The greatest() function returns the largest value among all the provided expressions. If any input is null, the function may skip null values or return null depending on the implementation.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Multiple Arguments: The function accepts two or more arguments and returns the maximum value among them.
  • Type Consistency: All arguments should be of compatible types for meaningful comparison.
  • Null Handling: If all inputs are null, the function returns null. If some inputs are null, the function typically returns the greatest non-null value.
  • String Comparison: When comparing strings, the function uses lexicographic (alphabetical) ordering.
  • Relationship to least(): The greatest() function is the counterpart of least(), which returns the smallest value.
  • Common Use Cases: This function is typically used within the alter stage for selecting the maximum value across multiple fields, implementing upper bounds, and data normalization.

Examples

Example 1: Find greatest among literal values

Goal: Find the largest value from a set of numeric literals.

XQL code:

dataset = xdr_data
| limit 1
| alter max_val = greatest(10, 25, 5, 42, 18)
| fields max_val

Explanation: The greatest() function compares all five literal values and returns 42, the largest among them.

Output:

MAX_VAL
42

Example 2: Find greatest value across multiple fields

Goal: Find the largest value among multiple numeric fields for each record.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter max_field = greatest(event_id, numeric_value, duration_seconds)
| fields event_id, numeric_value, duration_seconds, max_field
| limit 3

Explanation: This query compares the values of event_id, numeric_value, and duration_seconds for each record and returns the largest value in max_field.

Output:

EVENT_ID NUMERIC_VALUE DURATION_SECONDS MAX_FIELD
101 5.0 1.5 101
102 200.0 0.8 200.0
103 50.0 10.2 103

Example 3: Implement an upper bound using greatest

Goal: Ensure a value is at least a minimum threshold by using greatest() as a lower clamp.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter clamped_duration = greatest(duration_seconds, 1.0)
| fields event_id, duration_seconds, clamped_duration
| limit 3

Explanation: This query ensures that clamped_duration is at least 1.0 by returning the greater of duration_seconds and 1.0. Values below 1.0 are raised to 1.0, while values above remain unchanged.

Output:

EVENT_ID DURATION_SECONDS CLAMPED_DURATION
101 1.5 1.5
102 0.8 1.0
103 10.2 10.2

hierarchy_match

Use the hierarchy_match() function to determine whether an asset belongs to a specified node in the organizational (org) asset hierarchy. You can match against the hierarchy path (the ordered list of container names from the org root down to a specific node), a specific hierarchy node ID (id_path), or both. Calling the function with no arguments matches every asset (equivalent to *).

The function is only available when the org asset hierarchy is enabled for your tenant, and it operates on the asset hierarchy fields xdm.asset.hierarchy.path (the chain of container names joined by /) and xdm.asset.hierarchy.id_path (the ID of the deepest node in that path). It is intended for asset datasets (for example, asset_inventory) and is not supported inside correlation rules.

Syntax

hierarchy_match(<path_segments>, <id_path>)

Parameters

Name Type Required Description
path_segments array No An ordered list of the container names from the org root down to the target node (for example, ["Org", "ou1", "ou2"]). Segments are joined with / and matched as a path prefix, so the node and all of its descendants match. The path contains only container levels (organization, OUs, management groups, folders) — not the leaf account/project/subscription. If omitted (or an empty array), no path restriction is applied and all paths match. As a shorthand, passing a single string value is treated as the id_path argument.
id_path string No The resource ID of a specific hierarchy node — the ID of the deepest container in the path (for example, an OU ID, management-group ID, or folder ID). Use it to unambiguously target a node even when names are duplicated. When provided, it must match the asset's id_path. If omitted, no ID restriction is applied.

Returns

Type: boolean

Description: The hierarchy_match() function returns true if the asset belongs to the specified org hierarchy node (satisfying both the path and ID conditions that were supplied), and false otherwise.

Usage notes

  • Path vs. id_path: The path and id_path describe the same container node from two angles: path is the chain of container names, and id_path is the ID of the deepest node in that chain. Neither includes the account/project/subscription.
  • Disambiguating duplicate names: Use id_path when container names are ambiguous. For example, if two different OUs are both named Production, matching on the path name alone is ambiguous — supplying the OU's id_path pins the match to exactly the OU you mean.
  • Combinable, optional parameters: Both parameters are optional and can be combined:
    • hierarchy_match() — matches all assets (no restriction).
    • hierarchy_match(arraycreate("Org", "ou1")) — path-only match (the node and all its descendants).
    • hierarchy_match(null, "ou-a1b2-prod01") — ID-only match (match the node with this exact ID, on any path).
    • hierarchy_match("ou-a1b2-prod01") — shorthand for an ID-only match.
    • hierarchy_match(arraycreate("Org", "ou1"), "ou-a1b2-prod01") — matches only when both the path prefix and the ID condition are satisfied.
  • Prefix, case-sensitive, literal matching: The path match is a prefix match: it matches the exact path as well as any child path beneath it. It is case-sensitive and matches literal characters (wildcard characters such as % and _ in path segments are treated literally, not as wildcards).
  • Ambiguous scalar + ID is rejected: Passing a single scalar/string value together with an explicit id_path is ambiguous and is rejected with a validation error. Use an array for the first argument whenever you also supply an ID.
  • Negation: To negate a match (find assets that do not belong to a node), compare the result to false (for example, hierarchy_match(...) = false) or use != true.
  • Typical use: This function is typically used within the filter stage to scope results to an org hierarchy node, or within the alter stage to tag records with their hierarchy membership.
  • Cloud hierarchy structure: The org hierarchy mirrors the native resource-organization model of each cloud provider. In every case the hierarchy stops at the container level — the leaf account, project, or subscription is not part of the path or the id_path. The path_segments argument is the ordered list of the display names of the container levels (not their IDs, and not the leaf), and id_path is the ID of the deepest container node in the path. Matching on path_segments alone selects the target container node and all of its descendants, which is useful for scoping to an entire OU, management group, or folder subtree.

    Cloud Provider Container Levels (org root → node) Leaf (not part of hierarchy path/id_path) Example Path Example id_path (ID of deepest node)
    AWS Organization → Organizational Unit (OU) Account Acme Corp/Production OU ID: ou-a1b2-prod01
    Azure Management Group → (nested Management Groups) Subscription Tenant Root Group/Platform Management Group ID: mg-platform
    GCP Organization → Folder(s) Project acme.com/Production Folder ID: folders/574839201122

Examples

Example 1: Scope to an AWS organizational unit

Goal: Return only the assets that belong to the AWS Production OU under the Acme Corp organization, including every account nested beneath that OU.

XQL code:

config timeframe = 1d
| dataset = asset_inventory
| filter hierarchy_match(arraycreate("Acme Corp", "Production"))
| fields asset_id, asset_name, cloud_provider, xdm.asset.hierarchy.path
| limit 3

Explanation: You apply a path-only match in a filter stage. The path contains only the container levels (organization and OU), so matching on Acme Corp/Production returns every asset in any account under the Production OU (and its sub-OUs), because the match is a prefix match.

Output:

ASSET_ID ASSET_NAME CLOUD_PROVIDER XDM.ASSET.HIERARCHY.PATH
i-0a1b2c3d payments-api-01 AWS Acme Corp/Production
i-0e4f5g6h orders-worker-02 AWS Acme Corp/Production
i-0i7j8k9l billing-db-01 AWS Acme Corp/Production/Billing

Example 2: Disambiguate an OU by ID

Goal: Two OUs are both named Production under different parents. Return only the assets under the specific Production OU whose ID is ou-a1b2-prod01.

XQL code:

config timeframe = 1d
| dataset = asset_inventory
| filter hierarchy_match(arraycreate("Acme Corp", "Production"), "ou-a1b2-prod01") = true
| fields asset_id, asset_name, xdm.asset.hierarchy.path, xdm.asset.hierarchy.id_path
| limit 3

Explanation: Because the OU name alone is ambiguous, you combine the path prefix (Acme Corp/Production) with the OU's ID as id_path. An asset is returned only when its path starts with Acme Corp/Production and its id_path equals ou-a1b2-prod01, so only the intended OU is matched.

Output:

ASSET_ID ASSET_NAME XDM.ASSET.HIERARCHY.PATH XDM.ASSET.HIERARCHY.ID_PATH
i-0a1b2c3d payments-api-01 Acme Corp/Production ou-a1b2-prod01
i-0m1n2o3p payments-cache-01 Acme Corp/Production ou-a1b2-prod01
i-0q4r5s6t payments-lb-01 Acme Corp/Production ou-a1b2-prod01

Example 3: Scope to an Azure management group

Goal: Tag assets that live under the Azure Platform management group (any subscription beneath it), starting from the Tenant Root Group.

XQL code:

config timeframe = 1d
| dataset = asset_inventory
| alter in_platform_mg = hierarchy_match(arraycreate("Tenant Root Group", "Platform"))
| fields asset_id, cloud_provider, xdm.asset.hierarchy.path, in_platform_mg
| limit 3

Explanation: You use a path-only match in an alter stage. In Azure the path contains the management-group chain (Management Group → nested Management Groups) and stops above the subscription, so matching on Tenant Root Group/Platform returns true for every asset in any subscription nested under the Platform management group, and false otherwise.

Output:

ASSET_ID CLOUD_PROVIDER XDM.ASSET.HIERARCHY.PATH IN_PLATFORM_MG
vm-web-01 AZURE Tenant Root Group/Platform true
vm-api-02 AZURE Tenant Root Group/Platform/Prod true
vm-sales-03 AZURE Tenant Root Group/Business false

Example 4: Pin to a specific Azure management group by ID

Goal: Return only the assets under the Platform management group whose ID is mg-platform.

XQL code:

config timeframe = 1d
| dataset = asset_inventory
| filter hierarchy_match(arraycreate("Tenant Root Group", "Platform"), "mg-platform") = true
| fields asset_id, asset_name, xdm.asset.hierarchy.path, xdm.asset.hierarchy.id_path
| limit 3

Explanation: You combine the management-group path prefix (Tenant Root Group/Platform) with the management group's ID as id_path. Only assets whose path starts with Tenant Root Group/Platform and whose id_path equals mg-platform are returned — the ID pins the match to the exact management group.

Output:

ASSET_ID ASSET_NAME XDM.ASSET.HIERARCHY.PATH XDM.ASSET.HIERARCHY.ID_PATH
vm-web-01 payments-web-01 Tenant Root Group/Platform mg-platform
vm-web-02 payments-web-02 Tenant Root Group/Platform mg-platform
st-blob-01 payments-storage Tenant Root Group/Platform mg-platform

Example 5: Scope to a GCP folder

Goal: Return only the assets that belong to the GCP Production folder under the acme.com organization, including every project nested inside that folder.

XQL code:

config timeframe = 1d
| dataset = asset_inventory
| filter hierarchy_match(arraycreate("acme.com", "Production"))
| fields asset_id, asset_name, cloud_provider, xdm.asset.hierarchy.path
| limit 3

Explanation: In GCP the path contains the organization and its folders and stops above the project. A path-only match on acme.com/Production returns every asset in any project inside the Production folder (and any sub-folders), because the match is a prefix match.

Output:

ASSET_ID ASSET_NAME CLOUD_PROVIDER XDM.ASSET.HIERARCHY.PATH
gce-inst-01 payments-prod-vm GCP acme.com/Production
gce-inst-02 orders-prod-vm GCP acme.com/Production
gcs-bucket-01 billing-prod-data GCP acme.com/Production/Billing

Example 6: Pin to a specific GCP folder by ID

Goal: Return only the assets under the Production folder whose ID is folders/574839201122.

XQL code:

config timeframe = 1d
| dataset = asset_inventory
| filter hierarchy_match(arraycreate("acme.com", "Production"), "folders/574839201122") = true
| fields asset_id, asset_name, xdm.asset.hierarchy.path, xdm.asset.hierarchy.id_path
| limit 3

Explanation: You combine the folder path prefix (acme.com/Production) with the folder's ID as id_path. Only assets whose path starts with acme.com/Production and whose id_path equals folders/574839201122 are returned, pinning the match to the exact folder even if another folder shares the same name.

Output:

ASSET_ID ASSET_NAME XDM.ASSET.HIERARCHY.PATH XDM.ASSET.HIERARCHY.ID_PATH
gce-inst-01 payments-prod-vm acme.com/Production folders/574839201122
gce-disk-01 payments-prod-disk acme.com/Production folders/574839201122
gcs-bucket-02 payments-prod-bucket acme.com/Production folders/574839201122

Example 7: Exclude a hierarchy node (negation)

Goal: Return all assets except those in the Acme Corp/Sandbox OU.

XQL code:

config timeframe = 1d
| dataset = asset_inventory
| filter hierarchy_match(arraycreate("Acme Corp", "Sandbox")) = false
| fields asset_id, cloud_provider, xdm.asset.hierarchy.path
| limit 3

Explanation: Comparing the result of hierarchy_match() to false inverts the match, so every asset that does not live under the Acme Corp/Sandbox container is returned. This is useful for excluding non-production or sandbox scopes from a query.

Output:

ASSET_ID CLOUD_PROVIDER XDM.ASSET.HIERARCHY.PATH
i-0a1b2c3d AWS Acme Corp/Production
vm-web-01 AZURE Tenant Root Group/Platform
gce-inst-01 GCP acme.com/Production
  • Stages: filter, alter, fields
  • Functions: arraycreate(), wildcard_match, coalesce()
  • Datasets: asset_inventory

if

Use the if() function to evaluate a boolean condition and return a specific value based on whether the result is true or false.

Syntax

if (<boolean expression>, <true_return_expression>[, <false_return_expression>])

Parameters

Name Type Required Description
boolean_expression boolean Yes The condition that the function evaluates to either true or false.
true_return_expression string, integer, float, boolean Yes The value returned if the boolean expression evaluates to true.
false_return_expression string, integer, float, boolean No The value returned if the boolean expression evaluates to false. If omitted, the function returns NULL.

Returns

The if() function returns the value of the true_return_expression if the condition is met. If the condition is not met, it returns the false_return_expression, or NULL if the false return expression is not provided.

Usage notes

  • The first parameter must always be a boolean expression that evaluates to either true or false.
  • If the false_return_expression is omitted and the boolean expression evaluates to false, the function returns NULL.
  • It is a best practice for the true_return_expression and false_return_expression to return compatible data types (for example, both strings or both integers) to ensure predictable results.
  • The function supports nested if/else logic, allowing you to chain multiple conditions sequentially.

Examples

Example 1: Basic if with a boolean field

Goal: Categorize events as "Success" or "Failure" based on an existing boolean field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_outcome_status = if(is_successful = true, "Success", "Failure") 
| fields event_id, is_successful, event_outcome_status 
| limit 5 

Explanation: The query evaluates the is_successful field. If it is true, the new field event_outcome_status is set to "Success"; otherwise, it is set to "Failure".

Output:

EVENT_ID IS_SUCCESSFUL EVENT_OUTCOME_STATUS
101 true "Success"
102 false "Failure"
103 true "Success"
104 true "Success"
105 true "Success"

Example 2: if with a numeric comparison

Goal: Classify events based on a numerical threshold using the duration_seconds field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter duration_category = if(duration_seconds > 5.0, "Long Duration", "Short Duration") 
| fields event_id, duration_seconds, duration_category 
| limit 5 

Explanation: The query checks if duration_seconds is greater than 5.0. If true, duration_category is labeled "Long Duration"; otherwise, it is labeled "Short Duration".

Output:

EVENT_ID DURATION_SECONDS DURATION_CATEGORY
101 1.5 "Short Duration"
102 0.8 "Short Duration"
103 10.2 "Long Duration"
104 0.1 "Short Duration"
105 5.0 "Short Duration"

Example 3: if with string matching

Goal: Categorize event_id into distinct ranges using nested conditions to assign a descriptive string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_category = if(
    event_id <= 103, "Low Event ID", 
    event_id <= 106, "Medium Event ID", 
    "High Event ID") 
| fields event_id, event_category 
| limit 5 

Explanation: The query evaluates conditions sequentially. If event_id is 103 or less, it assigns "Low Event ID". If that fails but it is 106 or less, it assigns "Medium Event ID". Otherwise, it assigns "High Event ID".

Output:

EVENT_ID EVENT_CATEGORY
101 Low Event ID
102 Low Event ID
103 Low Event ID
104 Medium Event ID
105 Medium Event ID

Example 4: if with nested if for complex logic

Goal: Create granular categories based on both the is_successful status and duration_seconds using nested if() functions.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter detailed_status = if( 
    is_successful = true, 
    if(
        duration_seconds > 10.0, 
        "Success - Long Duration", 
        "Success - Short Duration"
    ), 
    "Failure" 
) 
| fields event_id, is_successful, duration_seconds, detailed_status 
| limit 5 

Explanation: If is_successful is true, a nested if checks duration_seconds. If duration is greater than 10.0, it returns "Success - Long Duration", otherwise "Success - Short Duration". If is_successful is false, it returns "Failure".

Output:

EVENT_ID IS_SUCCESSFUL DURATION_SECONDS DETAILED_STATUS
101 true 1.5 "Success - Short Duration"
102 false 0.8 "Failure"
103 true 10.2 "Success - Long Duration"
104 true 0.1 "Success - Short Duration"
105 true 5.0 "Success - Short Duration"

Example 5: if returning numeric values with multiplication

Goal: Conditionally modify a numeric value using the multiply() function based on an event ID threshold.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter threshold_event_id = if(event_id > 104, multiply(event_id, 10), event_id) 
| fields event_id, threshold_event_id 
| limit 5 

Explanation: If event_id is greater than 104, the new field threshold_event_id contains the event_id multiplied by 10. Otherwise, it contains the original event_id.

Output:

EVENT_ID THRESHOLD_EVENT_ID
101 101
102 102
103 103
104 104
105 1050

Example 6: if with omitted false return expression

Goal: Demonstrate that if() returns NULL when the condition is false and no false return expression is specified.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter google_traffic_tag = if(dst_domain = "www.google.com", "Google Traffic") 
| fields event_id, dst_domain, google_traffic_tag 
| limit 5 

Explanation: The query checks if dst_domain equals "www.google.com". If true, it returns "Google Traffic". Because no false return value is provided, it returns NULL for all other domains.

Output:

EVENT_ID DST_DOMAIN GOOGLE_TRAFFIC_TAG
101 "ec2.amazonaws.com" NULL
102 "sts.amazonaws.com" NULL
103 "www.google.com" "Google Traffic"
104 "dropbox.com" NULL
105 NULL NULL

Example 7: Remove file extensions conditionally

Goal: Use conditional logic to check if a process name contains ".exe" and remove that substring if present, otherwise return the lowercase process name.

XQL Code:

dataset = sample_xql_raw 
| fields action_process_image_name as apin 
| filter apin != null 
| alter remove_exe_process = 
    if(lowercase(apin) contains ".exe",  // boolean expression
       replace(lowercase(apin),".exe",""), // return if true
       lowercase(apin))  // return if false
| limit 10

Explanation: The query first filters for records where the process image name is not null and aliases it as apin. The query then uses the if() function to evaluate a boolean expression: whether the lowercase version of the field apin contains the string ".exe".

  • If the condition is true, the replace() function is called to find the ".exe" substring and substitute it with an empty string, effectively removing the extension.
  • If the condition is false, it returns the lowercase name as is. The result of this logic is stored in a new field called remove_exe_process.

Output:

apin remove_exe_process
cmd.exe cmd
POWERSHELL.EXE powershell
svchost.exe svchost
python3 python3

Example: Categorize local IP addresses using conditional logic

Goal: Evaluate the action_local_ip field from the last 7 days of sample_xql_raw to identify and label specific local IP ranges (10.x.x.x, 172.x.x.x, or 192.168.x.x) in a new column, returning null if no match is found.

XQL Code:

config timeframe = 7d 
| dataset = sample_xql_raw 
| limit 1
| alter 
    check_ip = if(action_local_ip ~= "^10", //boolean expression1
               "Local 10", // true return expression1
               action_local_ip ~= "^172", //boolean expression2
               "Local 172 ?", //true return expression2
               action_local_ip ~= "^192\.168", //boolean expression3
               "Local 192") //true return expression3

Explanation: The query targets the sample_xql_raw dataset for a 7-day period and limits the output to a single record. The query uses the alter stage to create a new field, check_ip, driven by the if() function. The function sequentially evaluates three regular expression matches (~=) against the action_local_ip field:

  • If the IP starts with "10", it returns "Local 10".
  • If the first condition is false and the IP starts with "172", it returns "Local 172 ?".
  • If the previous conditions are false and the IP starts with "192.168", it returns "Local 192".
  • If none of these conditions are met, the function returns null.

Output:

action_local_ip check_ip
192.168.1.50 Local 192

incidr

Use the incidr() function to determine if an IPv4 address is contained within one or more specified Classless Inter-Domain Routing (CIDR) blocks. The function returns true if the address falls within any of the defined ranges, and false otherwise.

Syntax

incidr (<ipv4_address_field>, "<cidr_range1>[, <cidr_range2>...]")

Parameters

Name Type Required Description
ipv4_address_field string Yes The field containing the IPv4 address (or a string literal) to evaluate.
cidr_ranges string Yes A string literal containing one or more IPv4 ranges in CIDR notation (for example, "192.168.1.0/24"), separated by commas.

Returns

The incidr() function returns a boolean value (true or false).

Usage notes

  • The first parameter must contain an IPv4 address contained in an IPv4 field. For production purposes, this IPv4 address will normally be carried in a field that you retrieve from a dataset. For manual usage, assign the IPv4 address to a field, and then use that field with this function.
  • This function is specifically designed for IPv4 addresses. For IPv6, use the incidr6() function.
  • You can define multiple CIDR ranges within the second parameter string by separating them with commas.
  • When multiple CIDR ranges are provided, the function uses logical OR logic. If the IP address falls within any of the specified ranges, the function returns true.
  • This function is commonly used within the filter stage to narrow down results based on network segments (for example, separating internal traffic from external traffic).
  • To check if an IP is not in a range, you can use the syntax not incidr().

Examples

Example 1: Check if IP is in a single CIDR block (match)

Goal: Filter for records where the ipv4_address falls within a specific private network range (192.168.1.0/24).

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter ipv4_address incidr("192.168.1.0/24") 
| fields event_id, ipv4_address 
| limit 5 

Explanation: The query evaluates the ipv4_address for each record. The query returns the record with event_id 101 because its IP (192.168.1.10) is within the 192.168.1.0/24 range.

Output:

EVENT_ID IPV4_ADDRESS
101 192.168.1.10

Example 2: Check if IP is in a single CIDR block (no match)

Goal: Filter for records where the ipv4_address falls within the 10.0.0.0/8 private network range.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter ipv4_address incidr("10.0.0.0/8") 
| fields event_id, ipv4_address 
| limit 5 

Explanation: The query checks if the ipv4_address is in the 10.0.0.0/8 range. The query returns event_id 102 because its IP (10.0.0.5) falls within this block. Other records with different IPs (like 192.168.x.x) are excluded.

Output:

EVENT_ID IPV4_ADDRESS
102 10.0.0.5

Example 3: Check if IP address is in multiple CIDR blocks (logical OR)

Goal: Filter for records where the ipv4_address falls within either the 10.0.0.0/8 range or the 192.168.1.0/24 range.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter ipv4_address incidr("10.0.0.0/8, 192.168.1.0/24") 
| fields event_id, ipv4_address 
| limit 5 

Explanation: The query uses a comma-separated list of CIDRs. The query returns event_id 101 because 192.168.1.10 matches the second CIDR, and event_id 102 because 10.0.0.5 matches the first CIDR. This demonstrates the logical OR behavior.

Output:

EVENT_ID IPV4_ADDRESS
101 192.168.1.10
102 10.0.0.5

Example 4: Exclude IP addresses in multiple CIDR blocks (not incidr)

Goal: Filter for records where the ipv4_address does not fall within common private IPv4 ranges, effectively filtering for public IPs.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter ipv4_address not incidr("10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16") 
| fields event_id, ipv4_address 
| limit 5 

Explanation: The query uses not incidr to exclude any IP addresses found in the specified private ranges. The query returns event_id 106 because its IP (203.0.113.15) is a public address and does not match any of the provided private CIDRs.

Output:

EVENT_ID IPV4_ADDRESS
106 203.0.113.15

incidr6

Use the incidr6() function to determine if an IPv6 address falls within one or more specified Classless Inter-Domain Routing (CIDR) blocks.

Syntax

incidr6 (<ipv6_address_field>, "<cidr6_range1>[, <cidr6_range2>...]")

Parameters

Name Type Required Description
ipv6_address_field string, ip Yes The IPv6 address field (or explicit string literal) to evaluate.
cidr6_range string Yes One or more IPv6 CIDR ranges, provided as a comma-separated string enclosed in double quotes (for example, "2001:db8::/32" or "2001:db8::/32, 2001:db8:cafe::/48").

Returns

The incidr6() function returns a boolean value (true or false). The function returns true if the address falls within any of the defined ranges, and false otherwise.

Usage notes

  • This function is designed specifically for IPv6 addresses. Use incidr() for IPv4 addresses.
  • The first parameter must contain an IPv6 address contained in an IPv6 field. For production purposes, this IPv6 address will normally be carried in a field that you retrieve from a dataset. For manual usage, assign the IPv6 address to a field, and then use that field with this function.
  • When multiple CIDR ranges are provided in the second parameter (separated by commas), the function applies a logical OR operation. If the IPv6 address matches any of the listed ranges, the function returns true.
  • This function is commonly used within the filter stage to narrow down results based on network segments or specific IP ranges.

Examples

Example 1: Check against a single IPv6 CIDR

Goal: Filter for records where the ipv6_address falls within a specific /32 global unicast CIDR block.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter ipv6_address incidr6("2001:0db8::/32") 
| fields event_id, ipv6_address 
| limit 5 

Explanation: The query evaluates the ipv6_address field for each record. The query returns only those records where the address (for example, 2001:0db8::1) falls within the 2001:0db8::/32 range.

Output:

EVENT_ID IPV6_ADDRESS
103 2001:0db8::1
107 2001:0db8:cafe::1
109 2001:0db8:1234::abcd

Example 2: Check against multiple IPv6 CIDRs

Goal: Filter for records where the ipv6_address falls within either of the specified IPv6 CIDR ranges.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter ipv6_address incidr6("2001:0db8:cafe::/48, 2001:0db8:1234::/48") 
| fields event_id, ipv6_address 
| limit 5 

Explanation: The query uses a comma-separated list of CIDRs. The query returns records where the ipv6_address matches either the first range (2001:0db8:cafe::/48) OR the second range (2001:0db8:1234::/48). Event ID 107 matches the first, and Event ID 109 matches the second.

Output:

EVENT_ID IPV6_ADDRESS
107 2001:0db8:cafe::1
109 2001:0db8:1234::abcd

incidrlist

Use the incidrlist() function to check if all IP addresses provided in a comma-separated list are contained within a given IPv4 CIDR range.

Syntax

incidrlis (<IP_address list>, <CIDR_range>)

Parameters

Name Type Required Description
IP_address list string Yes A string containing one or more IPv4 addresses, separated by commas.
CIDR_range string Yes A string literal specifying an IPv4 range in CIDR notation (for example, "192.168.1.0/24").

Returns

The incidrlist() function returns a boolean value (true or false).

Usage notes

  • incidrlist() returns true only if all IP addresses within the address list fall within the specified CIDR range.
  • If even one IP address in the list is outside the CIDR, the function returns false.
  • The input CIDR_range only accepts a single CIDR range. If more are provided, the query fails.

Examples

Example 1: All IP addresses in list match the CIDR range

Goal: Check if a list of IP addresses are all within a specific subnet.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter all_ips_in_range = incidrlist("192.168.1.10,192.168.10.20", "192.168.0.0/16") 
| fields event_id, all_ips_in_range 
| limit 3 

Explanation: For every record, the incidrlist() function evaluates if both 192.168.1.10 and 192.168.10.20 are within the 192.168.0.0/16 range. Because both are private IP addresses within this broader range, the function consistently returns true.

Output:

EVENT_ID ALL_IPS_IN_RANGE
101 true
102 true
103 true

Example 2: Not all IP addresses in list match the CIDR range

Goal: Check a list containing a public IP against a private CIDR range.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter not_all_ips_in_range = incidrlist("192.168.1.10,203.0.113.15", "192.168.0.0/16") 
| fields event_id, not_all_ips_in_range 
| limit 3 

Explanation: The incidrlist() function checks each IP address in the list. Because 203.0.113.15 is not within 192.168.0.0/16, the condition that all IP addresses must be in range is not met, resulting in false for every record.

Output:

EVENT_ID NOT_ALL_IPS_IN_RANGE
101 false
102 false
103 false

Example: Check an array of IP addresses against a CIDR range

Goal: Evaluate whether any IP address within an array matches a specific CIDR block by first converting the array into a comma-separated string.

XQL Code:

dataset = panw_ngfw_traffic_raw 
| filter dest_ip != null
| comp values(dest_ip) as dips by source_ip, action
| alter dips = arraystring(dips, ", ")
| alter inrange = incidrlist(dips, "192.168.10.0/24")
| fields source_ip, action, dips, inrange
| limit 100

Explanation: The query targets the panw_ngfw_traffic_raw dataset, filtering out records where the destination IP is null. The query then uses the comp stage to aggregate all unique dest_ip values into an array named dips, grouped by source_ip and action.

  • Since the incidrlist() function requires a string input, the arraystring() function converts the dips array into a single string with values separated by a comma and a space.
  • The incidrlist() function then evaluates this string to determine if any of the IP addresses contained within it fall inside the "192.168.10.0/24" network range.
  • The result is stored in the inrange field as a boolean (true or false).

Output:

source_ip action dips inrange
10.1.1.5 allow 192.168.10.15, 8.8.8.8, 172.16.0.1 true
10.1.1.10 deny 1.1.1.1, 10.50.50.2 false

int_to_ip

Use the int_to_ip() function to convert a signed integer representation of an IPv4 address into its standard dotted-decimal string equivalent.

Syntax

int_to_ip(<IPv4_integer>)

Parameters

Name Type Required Description
IPv4_integer integer Yes The integer value that represents an IPv4 address.

Returns

The int_to_ip() function returns the corresponding IPv4 address as a string.

Usage notes

  • This function is the inverse of the ip_to_int() function, which converts a string representation of an IPv4 address to an integer.
  • The function safely handles signed integers, meaning it can process both positive and negative integer inputs to produce valid IP address strings.
  • Ideally, this function is utilized within the alter stage to create new fields or modify existing ones based on the converted IP address.

Examples

Example 1: Convert standard positive integer to IPv4

Goal: Convert a standard positive integer representing an IPv4 address to its dotted-decimal string form.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter converted_ip = int_to_ip(75643532) 
| fields event_id, converted_ip 
| limit 3 

Explanation: The query creates a new field converted_ip by converting the integer 75643532 to its IPv4 string equivalent, which is "4.130.58.140".

Output:

EVENT_ID CONVERTED_IP
101 4.130.58.140
102 4.130.58.140
103 4.130.58.140

Example 2: Convert negative integer to IPv4

Goal: Convert a negative integer, which represents an IPv4 address in a different byte order, to its corresponding dotted-decimal string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter converted_ip = int_to_ip(-75643532) 
| fields event_id, converted_ip 
| limit 3 

Explanation: The query creates a new field converted_ip by converting the integer -75643532 to its IPv4 string equivalent, which is "251.125.197.116".

Output:

EVENT_ID CONVERTED_IP
101 251.125.197.116
102 251.125.197.116
103 251.125.197.116

Example 3: Convert zero to IPv4

Goal: Convert the integer 0 to its IPv4 address string representation.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter converted_ip = int_to_ip(0) 
| fields event_id, converted_ip 
| limit 3 

Explanation: The query creates converted_ip by converting 0 to its IPv4 string equivalent, "0.0.0.0".

Output:

EVENT_ID CONVERTED_IP
101 0.0.0.0
102 0.0.0.0
103 0.0.0.0

Example 4: Convert large positive integer to IPv4 (broadcast address)

Goal: Convert the maximum unsigned 32-bit integer to its IPv4 address string representation, which corresponds to the broadcast address.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter converted_ip = int_to_ip(4294967295) // Converts the integer 4294967295 (2^32 - 1) to an IPv4 address
| fields event_id, converted_ip 
| limit 3 

Explanation: The query creates a new field converted_ip by converting the integer 4294967295 (which is ) to its IPv4 string equivalent, "255.255.255.255".

Output:

EVENT_ID CONVERTED_IP
101 255.255.255.255
102 255.255.255.255
103 255.255.255.255

ip_to_int

Use the ip_to_int() function to safely convert a string representation of an IPv4 address into its signed integer equivalent.

Syntax

ip_to_int (<IPv4_address>)

Parameters

Name Type Required Description
IPv4_address string Yes The string representation of a valid IPv4 address.

Returns

The ip_to_int() function returns the signed integer representation of the provided IPv4 address string.

Usage notes

  • This function is designed to safely convert a string representation of an IPv4 address into its signed integer equivalent.
  • This conversion is crucial for scenarios where numerical operations or comparisons are required on IP addresses, or for storing them in an integer format.
  • ip_to_int() is the inverse of int_to_ip(), which converts an integer representation of an IPv4 address back to its string format.
  • This function was previously known as safe_ip_to_int().
  • If the input IPv4_address is NULL, the function returns NULL.

Examples

Example 1: Converting a literal IPv4 address string

Goal: Convert a static string representation of an IPv4 address into its integer form.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter converted_ip_int = ip_to_int("48.49.50.51") // Converts the literal string "48.49.50.51" to an integer 
| fields event_id, converted_ip_int 
| limit 3 

Explanation: The query creates a new field converted_ip_int by converting the literal IPv4 string "48.49.50.51" to its integer equivalent, which is 808530483. This value is constant across all returned records.

Output:

EVENT_ID CONVERTED_IP_INT
101 808530483
102 808530483
103 808530483

Example 2: Converting an IPv4 address from a dataset field

Goal: Apply the function to the ipv4_address field present in the dataset.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter converted_ip_from_field = ip_to_int(ipv4_address) // Converts the ipv4_address field to an integer 
| fields event_id, ipv4_address, converted_ip_from_field 
| limit 6 

Explanation: For each record, ipv4_address is converted to its integer representation in the converted_ip_from_field. Records with a NULL ipv4_address (like event_id 103) will also result in a NULL converted_ip_from_field.

Output:

EVENT_ID IPV4_ADDRESS CONVERTED_IP_FROM_FIELD
101 192.168.1.10 3232235786
102 10.0.0.5 167772165
103 NULL NULL
104 172.31.255.255 2892780543
105 192.168.10.20 3232238100
106 203.0.113.15 3407889679

Example 3: Handling NULL input

Goal: Demonstrate behavior when provided with a NULL input, by filtering for records where ipv4_address is NULL.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter ipv4_address = null // Focus on records where ipv4_address is NULL 
| alter converted_null_ip_int = ip_to_int(ipv4_address) // Attempts conversion of NULL 
| fields event_id, ipv4_address, converted_null_ip_int 
| limit 3 

Explanation: As is standard behavior for XQL functions when encountering null inputs, ip_to_int() returns NULL when its input ipv4_address is NULL.

Output:

EVENT_ID IPV4_ADDRESS CONVERTED_NULL_IP_INT
103 NULL NULL
107 NULL NULL
109 NULL NULL

Example 4: Converting a private IPv4 address

Goal: Target a specific private IPv4 address from the dataset to demonstrate its conversion.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 102 // Selects event with a private IP: 10.0.0.5 
| alter private_ip_as_int = ip_to_int(ipv4_address) // Converts the private IPv4 string to integer 
| fields event_id, ipv4_address, private_ip_as_int 
| limit 1 

Explanation: The private IP address "10.0.0.5" is successfully converted to its corresponding integer value 167772165.

Output:

EVENT_ID IPV4_ADDRESS PRIVATE_IP_AS_INT
102 10.0.0.5 167772165

Example 5: Converting a public IPv4 address

Goal: Target a specific public IPv4 address from the dataset to demonstrate its conversion.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 106 // Selects event with a public IP: 203.0.113.15 
| alter public_ip_as_int = ip_to_int(ipv4_address) // Converts the public IPv4 string to integer 
| fields event_id, ipv4_address, public_ip_as_int 
| limit 1 

Explanation: The public IP address "203.0.113.15" is successfully converted to its corresponding integer value 3407889679.

Output:

EVENT_ID IPV4_ADDRESS PUBLIC_IP_AS_INT
106 203.0.113.15 3407889679

is_ipv4

Use the is_ipv4() function to determine if a string value represents a valid IPv4 address.

Syntax

is_ipv4(<string>)

Parameters

Name Type Required Description
string string Yes The string field or literal value to evaluate for IPv4 validity.

Returns

The is_ipv4() function returns a boolean value: true if the string is a valid IPv4 address, and false otherwise.

Usage Notes

The function expects a string input and will return NULL if the input field is NULL.

Validation is strictly for IPv4 dotted-decimal format (for example, "192.168.1.1").

This function is typically used within the alter stage to tag records or within the filter stage to isolate specific traffic types.

Examples

Example 1: Validate IPv4 Addresses in a Dataset

Goal: Identify which records in the dataset contain a valid IPv4 address in the ipv4_address field.

XQL Code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter valid_ip = is_ipv4(ipv4_address)
| fields event_id, ipv4_address, valid_ip
| limit 3

Explanation: You use the is_ipv4() function to check the ipv4_address field. For record 101, the is_ipv4() function returns true because "192.168.1.10" is a valid IPv4 address. For record 103, where the field is NULL, the is_ipv4() function returns NULL.

Output:

EVENT_ID IPV4_ADDRESS VALID_IP
101 192.168.1.10 true
102 10.0.0.5 true
103 NULL NULL

Example 2: Filtering for Valid IPv4 Traffic

Goal: Filter the result set to return only those records that have a valid IPv4 address.

XQL Code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter is_ipv4(ipv4_address) = true
| fields event_id, ipv4_address
| limit 2

Explanation: You apply the is_ipv4() function directly within a filter stage to exclude any records where the ipv4_address field does not contain a properly formatted IPv4 address.

Output:

EVENT_ID IPV4_ADDRESS
101 192.168.1.10
102 10.0.0.5

is_ipv6

Use the is_ipv6() function to determine if a string value represents a valid IPv6 address.

Syntax

is_ipv6(<string>)

Parameters

Name Type Required Description
string string Yes The string field or literal value to evaluate for IPv6 validity. The IPv6 address can be either an explicit string using quotes (for example, "2606:4700:4700::1111") or a string field.

Returns

The is_ipv6() function returns a boolean value: true if the string is a valid IPv6 address, and false otherwise.

Usage Notes

  • The function expects a string input and will return NULL if the input field is NULL or not present.
  • Validation is strictly for IPv6 address formats. The function will return false for standard IPv4 addresses (for example, "1.1.1.1").
  • This function is typically used within the alter stage to tag records, or within the filter stage to isolate specific IPv6 network traffic.

Examples

Example 1: Filter the IPv6 Addresses

Goal: Filter a dataset to evaluate an ip field and return only the records containing a valid IPv6 address.

XQL Code:

dataset = ips_test_raw
| alter IsIpv6 = is_ipv6(ip)
| filter IsIpv6

Explanation: You use the is_ipv6() function in the alter stage to check the ip field and assign the boolean result to a new field named IsIpv6. Next, you apply the filter stage to return only the rows where IsIpv6 evaluates to true. While the original dataset contained IPv4 addresses like "1.1.1.1" and "192.168.1.100" (which evaluate to false), the filtered output retains only the valid IPv6 addresses "FF0E::1" and "2606:4700:4700::1111".

Output:

_TIME IP _VENDOR _PRODUCT ISIPV6
Mar 26th 2025 19:26:07 FF0E::1 ips test true
Mar 26th 2025 19:26:07 2606:4700:4700::1111 ips test true

is_known_private_ipv4

Use the is_known_private_ipv4() function to determine if a string value represents a known private, non-routable IPv4 address.

Syntax

is_known_private_ipv4(<string>)

Parameters

Name Type Required Description
string string Yes The string field or literal value representing an IPv4 address to evaluate.

Returns

The is_known_private_ipv4() function returns a boolean value true if the string is a valid private IPv4 address (such as those defined in RFC 1918), and false otherwise.

Usage Notes

  • The function expects a string input and returns NULL if the input field is NULL or does not evaluate to a valid IPv4 address.
  • The function evaluates standard private and special-purpose IP address spaces, typically including 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, loopback addresses (127.0.0.0/8), and link-local addresses (169.254.0.0/16).
  • This function is frequently used within the alter stage to tag internal network traffic, or within the filter stage to exclude internal communication from external threat hunts.

Examples

Example 1: Identify Private IPv4 Addresses in a Dataset

Goal: Identify which records in the dataset contain a known private IPv4 address in the ipv4_address field.

XQL Code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter is_private = is_known_private_ipv4(ipv4_address)
| fields event_id, ipv4_address, is_private
| limit 3

Explanation: You use the is_known_private_ipv4() function to check the ipv4_address field. For record 101, the function returns true because "10.0.0.5" belongs to a private subnet. For record 102, it returns false because "8.8.8.8" is a public routable IP. For record 103, where the field is NULL, it returns NULL.

Output:

EVENT_ID IPV4_ADDRESS IS_PRIVATE
101 10.0.0.5 true
102 8.8.8.8 false
103 NULL NULL

Example 2: Filtering for External (Public) IPv4 Traffic

Goal: Filter the result set to return only those records that do not contain a private IPv4 address.

XQL Code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter is_known_private_ipv4(ipv4_address) = false
| fields event_id, ipv4_address
| limit 2

Explanation: You apply the is_known_private_ipv4() function directly within a filter stage and check for false to isolate external (public) IP addresses, filtering out any internal traffic.

Output:

EVENT_ID IPV4_ADDRESS
102 8.8.8.8
104 1.1.1.1

is_known_private_ipv6

Use the is_known_private_ipv6() function to determine if a string value represents a known private, non-routable IPv6 address.

Syntax

is_known_private_ipv6(\<string\>)

Parameters

Name Type Required Description
string string Yes The string field or literal value representing an IPv6 address to evaluate.

Returns

The is_known_private_ipv6() function returns a boolean value: true if the string is a valid private IPv6 address (such as those defined in RFC 4193 for Unique Local Addresses), and false otherwise.

Usage Notes

  • The function expects a string input and will return NULL if the input field is NULL or does not evaluate to a valid IPv6 address.
  • The function evaluates standard private and special-purpose IPv6 address spaces, typically including Unique Local Addresses (ULA) within the fc00::/7 block (which includes fd00::/8), loopback addresses (::1/128), and link-local addresses (fe80::/10).
  • This function is frequently used within the alter stage to tag internal network traffic, or within the filter stage to exclude internal communication from external threat hunts.

Examples

Example 1: Identify Private IPv6 Addresses in a Dataset

Goal: Identify which records in the dataset contain a known private IPv6 address in the ip field.

XQL Code:

dataset = ips_test_raw
| alter is_private = is_known_private_ipv6(ip)
| fields _time, ip, is_private
| limit 3

Explanation: You use the is_known_private_ipv6() function in the alter stage to check the ip field. For the first record, the function returns true because fc00::1 is a Unique Local Address (ULA) and thus a private subnet. For the second record, it returns false because 2606:4700:4700::1111 is a public routable IPv6 address. For the third record, where the field is an IPv4 address, it returns NULL.

Output:

_TIME IP IS_PRIVATE
Mar 26th 2025 19:26:07 fc00::1 true
Mar 26th 2025 19:26:07 2606:4700:4700::1111 false
Mar 26th 2025 19:26:07 10.0.0.5 NULL

Example 2: Filtering for External (Public) IPv6 Traffic

Goal: Filter a dataset to return only those records that contain a valid IPv6 address that is not a private address.

XQL Code:

dataset = ips_test_raw
| filter is_ipv6(ip) and is_known_private_ipv6(ip) = false
| fields _time, ip

Explanation: You apply both the is_ipv6() and is_known_private_ipv6() functions directly within a filter stage. By ensuring the address is a valid IPv6 format and checking for false on the private evaluation, you isolate external (public) IPv6 addresses, filtering out any internal traffic or standard IPv4 data.

Output:

_TIME IP
Mar 26th 2025 19:26:07 2606:4700:4700::1111
Mar 26th 2025 19:26:07 2001:db8:3333:4444:5555:6666:7777:8888

json_extract

Use the json_extract() function to extract a specific JSON object or array from within a larger JSON-formatted string by navigating a specified path.

Syntax

json_extract (<json_object_formatted_string>, <json_path>)

Syntactic Sugar Format

<json_object_formatted_string> -> <json_path>{}

Parameters

Name Type Required Description
json_object_formatted_string string Yes The field containing the JSON string from which to extract data. If the field is an object or array, use to_json_string() to convert it first.
json_path string Yes The path to the data within the JSON object. In regular syntax, it starts with $. In syntactic sugar, the $ is omitted. Use brackets ['key'] or ["key"] for keys with special characters.

Returns

The json_extract() function returns a string representation of the extracted JSON object or array.

Usage notes

  • Input requirement: The function requires a field that contains a JSON-formatted string. If your field is a native XQL object or array, you must convert it using to_json_string() before extraction.
  • Output format: The function always returns a string. If the path points to a scalar value (string, number, boolean), it returns that value quoted as a JSON literal string. For direct scalar extraction without quotes (for numbers/booleans), consider using json_extract_scalar().
  • Case sensitivity: JSON field names are case-sensitive. The key in your json_path must exactly match the case of the field name in the JSON object.
  • Handling special characters: When a field name in the path contains special characters like a dot (.) or colon (:):
    • Regular syntax: Use the syntax json_extract(<json_object_formatted_string>, "['<json_field>']").
    • Syntactic sugar syntax: Use the syntax <json_object_formatted_string> -> ["<json_field>"]{}.
  • Error handling: If the input string is not a valid JSON object, or if the specified json_path does not exist, the function returns NULL.

Examples

Example 1: Basic object property extraction

Goal: Extract a nested JSON object from a parent object.

XQL code:

Regular Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter extracted_user_info = json_extract(to_json_string(nested_json_data), "$.user") 
| fields event_id, nested_json_data, extracted_user_info 
| limit 1

Syntactic Sugar Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter extracted_user_info = to_json_string(nested_json_data) -> user{} 
| fields event_id, nested_json_data, extracted_user_info 
| limit 1

Explanation: For event_id 101, the nested_json_data field contains a user object. The query converts the field to a JSON string and extracts the entire nested object, including keys and values.

Output:

EVENT_ID NESTED_JSON_DATA EXTRACTED_USER_INFO
101 {"user": {"id": "U1", "name": "Alice"}, "session": {...}} {"id": "U1", "name": "Alice"}

Example 2: Extracting a deeper nested object property

Goal: Navigate deeper into a JSON structure to extract a specific nested object.

XQL code:

Regular Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter extracted_session_info = json_extract(to_json_string(nested_json_data), "$.session") 
| fields event_id, nested_json_data, extracted_session_info 
| limit 1

Syntactic Sugar Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter extracted_session_info = to_json_string(nested_json_data) -> session{} 
| fields event_id, nested_json_data, extracted_session_info 
| limit 1

Explanation: The query extracts the session object, which is nested within nested_json_data, holding the session details.

Output:

EVENT_ID NESTED_JSON_DATA EXTRACTED_SESSION_INFO
101 {"user": {...}, "session": {"start": "10:00", "type": "web"}} {"start": "10:00", "type": "web"}

Example 3: Extracting a JSON object from an array using index

Goal: Extract a specific JSON object from an array field using its index.

XQL code:

Regular Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter extracted_first_action_obj = json_extract(to_json_string(array_of_json_objects), "$[0]") 
| fields event_id, array_of_json_objects, extracted_first_action_obj 
| limit 1 

Syntactic Sugar Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter extracted_first_action_obj = to_json_string(array_of_json_objects) -> [0]{} 
| fields event_id, array_of_json_objects, extracted_first_action_obj 
| limit 1

Explanation: The field array_of_json_objects contains an array of objects. The path $[0] (or [0]{}) extracts the first object in the array as a string.

Output:

EVENT_ID ARRAY_OF_JSON_OBJECTS EXTRACTED_FIRST_ACTION_OBJ
101 [{"action": "read", "file": "doc1.txt"}, {"action": "write", ...}] {"action": "read", "file": "doc1.txt"}

Example 4: Extracting the entire JSON field content (root extraction)

Goal: Extract the complete content of a JSON-formatted field as a string.

XQL code:

Regular Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter extracted_full_json = json_extract(to_json_string(simple_json_data), "$") 
| fields event_id, simple_json_data, extracted_full_json 
| limit 1 

Syntactic Sugar Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter extracted_full_json = to_json_string(simple_json_data) -> {} 
| fields event_id, simple_json_data, extracted_full_json 
| limit 1

Explanation: Using $ or {} with an empty string as the path signifies that the entire JSON content is to be extracted and returned.

Output:

EVENT_ID SIMPLE_JSON_DATA EXTRACTED_FULL_JSON
101 {"status": "ok", "code": 200} {"status": "ok", "code": 200}

Example 5: Extraction with a key containing special characters

Goal: Extract data when a key name contains special characters like a dot (.).

XQL code:

Regular Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| limit 1 
| alter json_with_special_key = to_string("{\"device.info\": {\"model\": \"XDR Agent\", \"version\": \"8.0\"}}") 
| alter extracted_special_key_obj = json_extract(json_with_special_key, "$['device.info']") 
| fields event_id, json_with_special_key, extracted_special_key_obj 

Syntactic Sugar Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| limit 1 
| alter json_with_special_key = to_string("{\"device.info\": {\"model\": \"XDR Agent\", \"version\": \"8.0\"}}") 
| alter extracted_special_key_obj = json_with_special_key -> ["device.info"]{} 
| fields event_id, json_with_special_key, extracted_special_key_obj 

Explanation: The key device.info contains a dot. In regular syntax, it is enclosed in single quotes and brackets ['device.info']. In syntactic sugar, it uses double quotes and brackets ["device.info"].

Output:

EVENT_ID JSON_WITH_SPECIAL_KEY EXTRACTED_SPECIAL_KEY_OBJ
101 {"device.info": {"model": "XDR Agent", "version": "8.0"}} {"model": "XDR Agent", "version": "8.0"}

Example 6: Handling a path that does not exist

Goal: Demonstrate behavior when the specified JSON path is not found.

XQL code:

Regular Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 103 
| alter extracted_non_existent = json_extract(to_json_string(simple_json_data), "$.status") 
| fields event_id, simple_json_data, extracted_non_existent 
| limit 1 

Syntactic Sugar Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 103 
| alter extracted_non_existent = to_json_string(simple_json_data) -> status{} 
| fields event_id, simple_json_data, extracted_non_existent 
| limit 1

Explanation: For event_id 103, the simple_json_data field does not contain a status key. The function returns NULL.

Output:

EVENT_ID SIMPLE_JSON_DATA EXTRACTED_NON_EXISTENT
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL

Example 7: Handling non-JSON formatted string input

Goal: Demonstrate behavior when the input is not valid JSON.

XQL code:

Regular Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter non_json_input = event_description 
| alter result_from_non_json = json_extract(non_json_input, "$") 
| fields event_id, non_json_input, result_from_non_json 
| limit 1 

Syntactic Sugar Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter non_json_input = event_description 
| alter result_from_non_json = non_json_input -> {} 
| fields event_id, non_json_input, result_from_non_json 
| limit 1

Explanation: The event_description field is a plain string ("User login successful"), not a JSON structure. The function fails to parse it and returns NULL.

Output:

EVENT_ID NON_JSON_INPUT RESULT_FROM_NON_JSON
101 "User login successful" NULL

Example 8: Using to_json_string() for explicit type conversion

Goal: Ensure input is a valid JSON string by explicitly converting a native object field.

XQL code:

Regular Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter nested_data_as_string = to_json_string(nested_json_data) 
| alter extracted_user_id = json_extract(nested_data_as_string, "$.user.id") 
| fields event_id, nested_json_data, nested_data_as_string, extracted_user_id 
| limit 1 

Syntactic Sugar Syntax

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter nested_data_as_string = to_json_string(nested_json_data) 
| alter extracted_user_id = nested_data_as_string -> user.id{} 
| fields event_id, nested_json_data, nested_data_as_string, extracted_user_id 
| limit 1

Explanation: The nested_json_data field is an object. to_json_string() is used to convert it into a valid JSON string required by json_extract(). The value "U1" is extracted as the string "U1".

Output:

EVENT_ID NESTED_JSON_DATA NESTED_DATA_AS_STRING EXTRACTED_USER_ID
101 {"user": {"id": "U1", "name": "Alice"}, ...} {"user": {"id": "U1", "name": "Alice"}, ...} (as string) ""U1""

json_extract_array

Use the json_extract_array() function to accept a string that represents a JSON array and returns an XQL-native array. This function is crucial for transforming JSON-formatted array data into a format that XQL can natively process for further analysis.

Syntax

Regular syntax

json_extract_array (<json_array_string>, <json_path>)

Syntactic Sugar syntax

<json_array_string> -> <json_path>[]

Parameters

Name Type Required Description
json_array_string string Yes The string that represents a JSON array. To convert a field to a JSON-formatted string, use the to_json_string() function.
json_path string Yes The argument that identifies the data of the JSON object you want to extract using dot-notation. In regular syntax, the root is represented by $.

Returns

The json_extract_array() function returns an XQL-native array.

Usage notes

  • The function requires a string that represents a JSON array as input.
  • JSON field names are case-sensitive. The key-to-field pairing in your XQL query must be identical to the JSON for results to be found.
  • When a field in the <json_path> contains special characters like a dot (.) or colon (:), specific syntax is required:
    • Regular Syntax**: Use brackets with single quotes: json_extract_array(<json_array_string>, "['<json_field>']").
    • Syntactic Sugar**: Use brackets with double quotes: <json_array_string> -> ["<json_field>"][].
  • The $ symbol representing the root of the JSON structure is required in the regular syntax but is not required in the syntactic sugar format.

Examples

Example 1: Extracting a root-level JSON array

Goal: Extract a direct JSON array from the array_of_json_objects field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_array = json_extract_array(to_json_string(array_of_json_objects), "$") 
| fields event_id, array_of_json_objects, extracted_array 
| limit 2

Explanation: This query demonstrates extracting a direct JSON array. The syntactic sugar equivalent is to_json_string(array_of_json_objects) -> [].

Output:

EVENT_ID ARRAY_OF_JSON_OBJECTS EXTRACTED_ARRAY
101 [{"action": "read", "file": "doc1.txt"}, {"action": "write", "file": "report.log"}] [{"action": "read", "file": "doc1.txt"}, {"action": "write", "file": "report.log"}]
102 [{"event": "file_open", "path": "/etc/passwd"}] [{"event": "file_open", "path": "/etc/passwd"}]

Example 2: Extracting a nested array with special characters in key

Goal: Extract an array where a key in the path contains a special character (a dot), demonstrating proper escaping.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_ips = json_extract_array(to_json_string(json_with_nested_array), "$['device.info']['ip.addresses']") 
| fields event_id, json_with_nested_array, extracted_ips 
| limit 2

Explanation: The device.info and ip.addresses keys are enclosed in single quotes within brackets (['device.info']['ip.addresses']) because they contain a dot, as per XQL syntax rules for regular JSON path. The syntactic sugar equivalent is to_json_string(json_with_nested_array) -> ["device.info"]["ip.addresses"][].

Output:

EVENT_ID JSON_WITH_NESTED_ARRAY EXTRACTED_IPS
101 {"device.info": {"ip.addresses": ["172.16.6.7", "172.16.8.9"]}} ["172.16.6.7", "172.16.8.9"]
102 {"device.info": {"ip.addresses": ["10.1.2.3", "10.2.3.4"]}} ["10.1.2.3", "10.2.3.4"]

Example 3: Extracting an XQL numeric array converted to JSON string

Goal: Convert a native XQL array (numeric_codes) to a JSON string and then extract the entire array back.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_numeric = json_extract_array(to_json_string(numeric_codes), "$") 
| fields event_id, numeric_codes, extracted_numeric 
| limit 2

Explanation: The query converts the native array to a JSON string and then extracts it. The syntactic sugar equivalent is to_json_string(numeric_codes) -> [].

Output:

EVENT_ID NUMERIC_CODES EXTRACTED_NUMERIC
101 [13, -47, 29, 82, -15] [13, -47, 29, 82, -15]
102 [-21, 56, 13, -88, 42] [-21, 56, 13, -88, 42]

Example 4: Demonstrating XQL-native array output and element access

Goal: Showcase that json_extract_array() returns an XQL-native array that can be manipulated by other array functions like arrayindex().

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter ip_addresses_array = to_json_string(json_with_nested_array) -> ["device.info"]["ip.addresses"][]
| alter first_ip = arrayindex(ip_addresses_array, 0) 
| fields event_id, json_with_nested_array, ip_addresses_array, first_ip 
| limit 2

Explanation: The ip_addresses_array is successfully extracted as an XQL-native array using json_extract_array() (syntactic sugar syntax used here). Subsequently, arrayindex() is used to retrieve the first element from this XQL-native array.

Output:

EVENT_ID JSON_WITH_NESTED_ARRAY IP_ADDRESSES_ARRAY FIRST_IP
101 {"device.info": {"ip.addresses": ["172.16.6.7", "172.16.8.9"]}} ["172.16.6.7", "172.16.8.9"] "172.16.6.7"
102 {"device.info": {"ip.addresses": ["10.1.2.3", "10.2.3.4"]}} ["10.1.2.3", "10.2.3.4"] "10.1.2.3"

json_extract_scalar

Use the json_extract_scalar() function to retrieve a single, atomic value—such as a string, number, or boolean—from a JSON object.

Syntax

json_extract_scalar (<json_object_formatted_string>, <json_path>)

Syntactic Sugar syntax

<json_object_formatted_string> -> <field_path>

Parameters

Name Type Required Description
json_object_formatted_string string Yes The string representation of the JSON object.
json_path string Yes The path to the specific scalar value to extract, typically starting with $ to represent the root.

Returns

The json_extract_scalar() function returns the extracted value as a string. If the targeted JSON field is an object or an array, or if the path does not exist, the function returns NULL.

Usage notes

  • The input <json_object_formatted_string> must be a string representation of a JSON object. You may need to use to_json_string() to convert fields like nested_json_data before extraction.
  • This function always returns the extracted value as a string. To use the value as a number or boolean, you must chain functions like to_integer(), to_float(), or to_boolean().
  • JSON field names are case-sensitive. The key in your json_path must match the case in the JSON object exactly.
  • XQL supports a "syntactic sugar" format for this function: <json_object_formatted_string> -> <field_path>. In this format, the leading $ is not required.
  • When a field name within the <json_path> contains special characters like a dot (.) or colon (:):
    • In regular syntax, enclose the field in single quotes within brackets: json_extract_scalar(<json_object_formatted_string>, "['<json_field>']").
    • In Syntactic Sugar syntax, enclose the field in double quotes within brackets: <json_object_formatted_string> -> ["<json_field>"].

Examples

Example 1: Extracting a top-level string scalar value (status)

Goal: Extract a simple string value directly from the simple_json_data field using both regular syntax and syntactic sugar.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_status = json_extract_scalar(to_json_string(simple_json_data), "$.status") 
| fields event_id, simple_json_data, extracted_status 
| limit 2 

Explanation: This query converts simple_json_data to a JSON string and extracts the value associated with the "status" key.

Output:

event_id simple_json_data extracted_status
101 {"status": "ok", "code": 200} "ok"
102 {"status": "fail", "error": "access_denied"} "fail"

Example 2: Extracting a numeric scalar value (code) and converting to integer

Goal: Extract a numeric value (which returns as a string) and explicitly convert it to an integer.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_code_str = json_extract_scalar(to_json_string(simple_json_data), "$.code") 
| alter extracted_code_int = to_integer(extracted_code_str) 
| fields event_id, simple_json_data, extracted_code_str, extracted_code_int 
| limit 2 

Explanation: For event 101, the code "200" is extracted as a string and then converted to the integer 200. For event 102, the code field does not exist, so NULL is returned.

Output:

event_id simple_json_data extracted_code_str extracted_code_int
101 {"status": "ok", "code": 200} "200" 200
102 {"status": "fail", "error": "access_denied"} NULL NULL

Example 3: Extracting a nested string scalar value (user.name)

Goal: Traverse a nested JSON structure to extract a specific scalar value.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_username = json_extract_scalar(to_json_string(nested_json_data), "$.user.name") 
| fields event_id, nested_json_data, extracted_username 
| limit 2 

Explanation: For event 101, the query navigates to user.name and extracts "Alice". For event 102, the user object is not present, resulting in NULL.

Output:

event_id nested_json_data extracted_username
101 {"user": {"id": "U1", "name": "Alice"}, ...} "Alice"
102 {"process": {"name": "cmd.exe", "pid": 1234}, ...} NULL

Example 4: Handling special characters in nested key names

Goal: Extract a value where the key name contains a special character (a dot), requiring specific bracket notation.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| limit 1 
| alter test_json_data = to_json_string("{\"a.b\": {\"scalar_field\": \"Special Value\"}}") 
| alter extracted_special_char_regular = json_extract_scalar(test_json_data, "$['a.b'].scalar_field") 
| fields event_id, test_json_data, extracted_special_char_regular 

Explanation: The key 'a.b' is enclosed in single quotes within brackets ['a.b'] because it contains a dot, adhering to XQL's regular JSON path rules.

Output:

event_id test_json_data extracted_special_char_regular
101 {"a.b": {"scalar_field": "Special Value"}} "Special Value"

Example 5: Attempting to extract an object or array (returns NULL)

Goal: Demonstrate that the function returns NULL when the targeted path points to a complex structure (object or array) instead of a scalar.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_array_as_scalar = json_extract_scalar(to_json_string(array_of_json_objects), "$") 
| fields event_id, array_of_json_objects, extracted_array_as_scalar 
| limit 2 

Explanation: Attempting to extract the root $ of array_of_json_objects (which is an array) using json_extract_scalar results in NULL because it is designed only for scalar values.

Output:

event_id array_of_json_objects extracted_array_as_scalar
101 [{"action": "read", ...}] NULL
102 [{"event": "file_open", ...}] NULL

Example 6: Handling non-existent paths (returns NULL)

Goal: Demonstrate the behavior when the specified JSON path does not exist in the object.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_non_existent = json_extract_scalar(to_json_string(simple_json_data), "$.nonExistentField") 
| fields event_id, simple_json_data, extracted_non_existent 
| limit 2 

Explanation: Because nonExistentField is not present in the JSON structure, the function gracefully returns NULL.

Output:

event_id simple_json_data extracted_non_existent
101 {"status": "ok", "code": 200} NULL
102 {"status": "fail", "error": "access_denied"} NULL

Example 7: Filtering using an XQL-native datatype

Goal: Return the storage_device_drive_type value from the action_file_device_info field, and return the record if it is 1.

XQL code:

dataset = xdr_data   
| fields action_file_device_info as afdi   
| alter sdn = to_integer(json_extract_scalar(to_json_string(afdi), "$.storage_device_drive_type"))   
| filter sdn = 1   
| limit 10

Explanation: This query converts the action_file_device_info object to a JSON string using to_json_string(), and then uses json_extract_scalar() to extract the storage_device_drive_type value. Because the extraction returns a string, to_integer() is used to cast the value into an XQL-native integer datatype before the filter stage evaluates if the value equals 1.

Output:

AFDI SDN
{"storage_device_drive_type": 1, "vendor": "SanDisk"} 1
{"storage_device_drive_type": 1, "vendor": "Samsung"} 1

Example 8: Filtering using a string

Goal: Return the storage_device_drive_type value from the action_file_device_info field, and return the record if it matches the string value of "1".

XQL code:

dataset = xdr_data   
| fields action_file_device_info as afdi   
| alter sdn = json_extract_scalar(to_json_string(afdi), "$.storage_device_drive_type")   
| filter sdn = "1"   
| limit 10

Explanation: Similar to the first example, this query extracts the storage_device_drive_type value as a string. However, instead of converting the datatype, the filter stage evaluates the result directly against the string literal "1".

Output:

AFDI SDN
{"storage_device_drive_type": "1", "vendor": "SanDisk"} "1"
{"storage_device_drive_type": "1", "vendor": "Samsung"} "1"

Example 9: Filtering using Syntactic Sugar Format

Goal: Return the storage_device_drive_type value from the action_file_device_info field using the shorthand syntactic sugar format.

XQL code:

dataset = xdr_data  
| fields action_file_device_info as afdi  
| alter sdn = to_integer(to_json_string(afdi)->storage_device_drive_type)  
| filter sdn = 1  
| limit 10

Explanation: This query achieves the exact same result as Example 7 but utilizes the JSON extract operator (->) as syntactic sugar. This shorthand format replaces the explicit json_extract_scalar() function call, making the query more concise while still converting the extracted string to an integer for the final filter evaluation.

Output:

AFDI SDN
{"storage_device_drive_type": 1, "vendor": "SanDisk"} 1
{"storage_device_drive_type": 1, "vendor": "Samsung"} 1

json_extract_scalar_array

Use the json_extract_scalar_array() function to extract a JSON array containing scalar values (strings, numbers, booleans) from a JSON object and represent it as an XQL array of strings.

Syntax

json_extract_scalar_array (<json_array_string>, <json_path>)

When a Cortex Data Model (XDM) field is used in the <json_path> and contains a dot (.) character, for example xdm.source.host.device_id, use the syntax:

json_extract_scalar_array(<json_array_string>, "['<json_field>']")

Parameters

Name Type Required Description
json_array_string string Yes The string that represents a JSON array. If the input is a field, use to_json_string() to convert it first.
json_path string Yes The identifier of the data within the JSON object to extract, using dot-notation. In regular syntax, the root is represented by $. If a field name contains special characters (like . or :), enclose it in single quotes within brackets (for example, ['<json_field>']).

Returns

The json_extract_scalar_array() function returns an XQL-native array containing the extracted scalar values. Unlike json_extract_array(), the scalar values within the returned array are not enclosed in double quotes. If the target array does not exist, or the JSONPath is invalid, it returns NULL.

Usage notes

  • If the input field is not already a string representing a JSON object or array, you must use the to_json_string() function to convert it.
  • JSON field names are case-sensitive. The key-to-field pairing in your XQL query must be identical to the JSON for results to be found.
  • This function does not support a syntactic sugar format; you must use the regular syntax.
  • If a field in the <json_path> contains characters like a dot (.) or colon (:), you must use the bracket notation ['<json_field>']. Note that for XDM fields, paths requiring escaping for invalid JSON characters might be unsupported.

Examples

Example 1: Extracting a scalar array from a nested JSON field (with special characters in path)

Goal: Extract the ipv4_address array from the json_with_nested_array field, handling special characters in the JSON path.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter json_string_data = to_json_string(json_with_nested_array) 
| alter extracted_ips = json_extract_scalar_array(json_string_data, "$['device.info']['ip.addresses']") 
| fields event_id, json_with_nested_array, extracted_ips 
| limit 3 

Explanation: The query first converts the json_with_nested_array object into a JSON-formatted string using to_json_string(). The query then uses json_extract_scalar_array() to extract the array found at $['device.info']['ip.addresses']. The bracket notation is used because ip.addresses contains a dot. The resulting extracted_ips array contains scalar values without quotes.

Output:

EVENT_ID JSON_WITH_NESTED_ARRAY EXTRACTED_IPS
101 {"device.info": {"ip.addresses": ["172.16.6.7", "172.16.8.9"]}} [172.16.6.7, 172.16.8.9]
102 {"device.info": {"ip.addresses": ["10.1.2.3", "10.2.3.4"]}} [10.1.2.3, 10.2.3.4]
103 {"metadata": {"tags": ["critical", "event"], "version": "2.1"}} NULL

Example 2: Extracting an Array of String Tags

Goal: Extract the array of text tags associated with each alert from a JSON payload.

XQL Code:

config timeframe = 1d  
| dataset = sample_xql_raw  
| filter json_payload != null  
| alter tags_array = json_extract_scalar_array(json_payload, "$.alert.tags")  
| fields event_id, tags_array  
| limit 2

Explanation: You use the json_extract_scalar_array() function in the alter stage to target the $.alert.tags path within the json_payload string. The function extracts the JSON array of strings and converts it into a standard XQL array assigned to tags_array.

Output:

EVENT_ID TAGS_ARRAY
101 ![][image1]
102 ![][image2]

Example 3: Extracting a Mixed Scalar Array

Goal: Extract an array containing mixed scalar types (strings, numbers, booleans) and observe the string conversion.

XQL Code:

config timeframe = 1d\
| dataset = sample_xql_raw\
| filter metrics_payload != null\
| alter mixed_array = json_extract_scalar_array(metrics_payload, "$.device.status_codes")\
| fields event_id, mixed_array\
| limit 2

Explanation: The target JSONPath $.device.status_codes points to an array like ["active", 200, true]. The json_extract_scalar_array() function extracts these values and casts the number 200 and the boolean true into strings, resulting in an XQL array of strings: ["active", "200", "true"].

Output:

EVENT_ID MIXED_ARRAY
201 ![][image3]
202 ![][image4]

Example 4: Extract an array of strings using json_extract_scalar_array

Goal: Extract strings using json_extract_scalar_array function.

XQL Code:

dataset = xdr_data  
| alter parsed_action_processes = json_extract_scalar_array(action_processes, "$.*")   
| fields parsed_action_processes, action_processes   
| filter parsed_action_processes != null   
| limit 1

Explanation: The results return an array of strings in the parsed_action_processes field.

Output:

PARSED_ACTION_PROCESSES ACTION_PROCESSES
["123", "2039", "332", "0"] [123, 2039, 332, 0]

Example 5: Determine the string length using json_extract_scalar_array and array_length

Goal: Find the length of the string array by using json_extract_scalar_array and array_length functions.

XQL Code:

dataset = xdr_data  
| filter action_processes != null   
| alter processes_length = array_length(json_extract_scalar_array(action_processes, "$.*"))  
| fields action_processes, processes_length  
| limit 1

Explanation: Extracting the array to string format allows you to determine the array's length.

Output:

ACTION_PROCESSES PROCESSES_LENGTH
[123, 2039, 332, 0] 4

Example 6: Querying normalized network traffic

Goal: Find all blocked network connections across multiple firewall vendors using the unified XDM schema.

XQL Code:

config timeframe = 1d  
| dataset = xdm.network.session  
| filter xdm.event.outcome = "blocked" or xdm.event.outcome = "denied"  
| comp count(xdm.event.id) as block_count by xdm.source.ipv4, xdm.target.ipv4  
| sort desc block_count  
| limit 5

Explanation: By querying the xdm.network.session dataset, you search across all ingested network logs that have been mapped to the XDM schema, regardless of the original vendor. The query filters for blocked traffic using the normalized xdm.event.outcome field. The query then aggregates the count of blocked events by the normalized source and target IP addresses (xdm.source.ipv4, xdm.target.ipv4), sorts them to find the top offenders, and limits the output to the top 5.

Output:

XDM.SOURCE.IPV4 XDM.TARGET.IPV4 BLOCK_COUNT
192.168.1.50 8.8.8.8 4500
10.0.0.15 1.1.1.1 3200
172.16.5.100 9.9.9.9 1500
192.168.1.10 8.8.4.4 850
10.0.0.55 208.67.222.222 420

Example 7: Extracting XDM normalized fields

Goal: Extract the xdm.file.permissions.owner in the xdm.finding.normalized_fields array, which returns true when the string value is one of the following: r or w.

XQL Code:

dataset = findings   
| filter JSON_EXTRACT_SCALAR_ARRAY(xdm.finding.normalized_fields, "$['xdm.file.permissions.owner']") in ("r", "w")   
| fields xdm.finding.normalized_fields

Explanation: This query searches the findings dataset. The query uses the JSON_EXTRACT_SCALAR_ARRAY() function to extract the JSON array corresponding to the XDM field xdm.file.permissions.owner from within the xdm.finding.normalized_fields object. The filter stage evaluates to true if the extracted array contains the strings "r" or "w", returning only those relevant records to the result set.

Output:

_TIME XDM.FINDING.NORMALIZED_FIELDS
Jan 15th 2025 09:10:44 { "xdm.file.permissions.owner": [ "r", "w", "x" ] }

XQL JSON Functions Reference

Core Concept: Why XQL has four JSON extraction functions

Unlike dynamic JSON parsing in other query languages, XQL requires you to specify the expected data type at the time of extraction. You must choose the correct function based on what you are extracting:

  • json_extract() — Use when extracting a JSON object or when you need to preserve the full JSON structure of the value.
  • json_extract_scalar() — Use when extracting a primitive value (string, number, or boolean) and you need a clean, unquoted string — especially for filter comparisons.
  • json_extract_array() — Use when extracting a JSON array, including arrays of objects.
  • json_extract_scalar_array() — Use when extracting a JSON array of primitive values and you need clean, unquoted elements for comparisons.

json_extract()

The json_extract() function navigates and retrieves a specific value from a nested JSON object. The function drills down into a JSON string to surface a single piece of data as a new field. The json_extract() function always returns a string. If the extracted value is a string in the JSON, the result includes the literal double-quote characters (e.g., "\"Medium\""). This is different from json_extract_scalar(), which strips the quotes.

Constraint: The input field must be a valid JSON-formatted string. If the string is malformed or not in JSON format, the json_extract() function will return null. To convert a raw string field into JSON before extraction, use the to_json_string() function.

Syntax

Regular Syntax:

json_extract(field_name, "$.<json_path>")

Special characters syntax:

When a field in the JSON path contains reserved characters such as an at sign (@), dot (.), or colon (:), you must use bracket notation:

json_extract(field_name, "$[<json_path>]")

Parameters

Name Type Required Description
field_name string Yes The source field or expression containing a valid JSON-formatted string.
json_path string Yes A JSONPath expression specifying the location of the value to extract (e.g., "$.key", "$.nested.key", "$.array[0].field").

Return Value

The json_extract() function always returns a string:

  • String values → The json_extract() function returns the value with surrounding double quotes as literal characters (e.g., "\"Medium\"")
  • JSON object → The json_extract() function returns a JSON-formatted string (e.g., {"name":"John","age":30})
  • JSON array → The json_extract() function returns a JSON array string (e.g., ["a","b","c"])
  • Number → The json_extract() function returns a string representation (e.g., "42")
  • Boolean → The json_extract() function returns a string representation (e.g., "true")

The json_extract() function returns null if:

  • The path does not exist.
  • The input is not valid JSON.
  • The value is JSON null.

Examples

Example 1: Extract a nested user object

dataset = xdr_data
| alter user_details = json_extract(action_evtlog_data_fields, "$.user")

Input:

action_evtlog_data_fields
{"user":{"name":"admin","role":"superuser"},"status":"success"}
{"user":{"name":"jdoe","role":"viewer"},"status":"failed"}

Output:

action_evtlog_data_fields user_details
{"user":{"name":"admin","role":"superuser"},"status":"success"} {"name":"admin","role":"superuser"}
{"user":{"name":"jdoe","role":"viewer"},"status":"failed"} {"name":"jdoe","role":"viewer"}

Example 2: Extract a value from a key containing @

dataset = xdr_data
| alter email_from = json_extract(email_headers, "$[@from]")

Input:

email_headers
{"@from":"admin@company.com","@to":"user@company.com","subject":"Alert"}

Output:

email_headers email_from
{"@from":"admin@company.com","@to":"user@company.com","subject":"Alert"} "\"admin@company.com\""

Syntactic Sugar alternative

For a more concise query, XQL supports a Syntactic Sugar format using the -> operator with {} suffix. This compiles to the exact same json_extract() engine but improves human readability for simple paths.

Syntactic Sugar format:

field_name -> <json_path>{}

Special characters Syntactic Sugar format:

field_name -> ["<json_path>"]{}

Syntactic Sugar Example:

dataset = xdr_data
| alter user_details = action_evtlog_data_fields -> user{}

Notes

  • Use json_extract() when you need to preserve the JSON type of the extracted value, especially when the result is a JSON object or array that you plan to further process.
  • For scalar values (strings, numbers, booleans), prefer json_extract_scalar() for cleaner output without JSON quoting.
  • Do not use json_extract() when the result will be used in a filter comparison — the literal JSON quotes will break the comparison. Use json_extract_scalar() instead.

json_extract_scalar(): How to Extract Plain Strings, Numbers, and Booleans

The json_extract_scalar() function retrieves a specific scalar value (string, number, or boolean) from a JSON string and returns it as a clean, unquoted string.

Unlike dynamic JSON parsing in other languages, XQL requires you to use json_extract_scalar() specifically when you need values stripped of literal JSON quotes, making it the required choice for filter comparisons or type-casting operations.

Constraint: The input field must be a valid JSON-formatted string. If the string is malformed, not in JSON format, or if the target path points to a complex object or array, the json_extract_scalar() function will return null. To convert a raw string field into JSON before extraction, use the to_json_string() function.

Canonical Syntax

Regular Syntax:

json_extract_scalar(field_name, "$.<json_path>")

Special characters syntax:

When a field in the JSON path contains reserved characters such as an at sign (@), dot (.), or colon (:), you must use bracket notation:

json_extract_scalar(field_name, "$[<json_path>]")

Parameters

Name Type Required Description
field_name string Yes The source field or expression containing a valid JSON-formatted string.
json_path string Yes A JSONPath expression pointing to a scalar value (string, number, or boolean).

Return Value

The json_extract_scalar() function returns the requested scalar value as a plain string with literal JSON quotes stripped:

  • String → The json_extract_scalar() function returns the value without surrounding quotes (e.g., "MacOSX" instead of "\"MacOSX\"")
  • Number → The json_extract_scalar() function returns the value as its string representation (e.g., "443")
  • Boolean → The json_extract_scalar() function returns the value as its string representation (e.g., "true")

The json_extract_scalar() function returns null if:

  • The path does not exist.
  • The value at the target path is a JSON object or a JSON array (non-scalar).
  • The input field is not valid JSON.

Examples

Example 1: Extracting multiple scalar fields for a filter operation

Explanation: Because we intend to use the extracted event_type in a filter statement (filter event_type = "login"), we must use json_extract_scalar() instead of json_extract(). The json_extract_scalar() function strips the literal JSON quotes, preventing quote-matching errors during the boolean comparison. If json_extract() were used here, the value would be "\"login\"" and the filter event_type = "login" would never match.

dataset = xdr_data
| alter
    username   = json_extract_scalar(action_evtlog_data_fields, "$.user.name"),
    event_type = json_extract_scalar(action_evtlog_data_fields, "$.event.type"),
    source_ip  = json_extract_scalar(action_evtlog_data_fields, "$.event.src")
| filter event_type = "login"

Input:

action_evtlog_data_fields
{"user":{"name":"admin"},"event":{"type":"login","src":"10.0.0.1"}}
{"user":{"name":"jdoe"},"event":{"type":"login","src":"192.168.1.5"}}
{"user":{"name":"svc_acct"},"event":{"type":"logout","src":"10.0.0.2"}}

Output (after filter):

username event_type source_ip
admin login 10.0.0.1
jdoe login 192.168.1.5

Example 2: Extracting a value from a key containing special characters

Explanation: The target JSON key @from contains an at-sign. Therefore, the json_extract_scalar() function must use bracket notation ($[@from]) to successfully retrieve the clean string.

dataset = xdr_data
| alter sender = json_extract_scalar(email_headers, "$[@from]")

Input:

email_headers
{"@from":"admin@company.com","@to":"user@company.com","subject":"Alert"}

Output:

email_headers sender
{"@from":"admin@company.com","@to":"user@company.com","subject":"Alert"} admin@company.com

Note: Unlike json_extract(), the output does NOT include surrounding double quotes. The value is returned as a clean string, ready for direct comparisons and filtering.

Syntactic Sugar alternative

For a more concise query, XQL supports a Syntactic Sugar format using the -> operator. This compiles to the exact same json_extract_scalar() engine but improves human readability for simple paths.

Syntactic Sugar format:

field_name -> <json_path>

Special characters Syntactic Sugar format:

field_name -> ["<json_path>"]

Syntactic Sugar Example:

dataset = xdr_data
| alter
    username   = action_evtlog_data_fields -> user.name,
    event_type = action_evtlog_data_fields -> event.type
| filter event_type = "login"

Notes

  • Unlike json_extract(), the json_extract_scalar() function strips JSON string quotes from the result, making it ideal for string comparisons and human-readable output.
  • If the target value is an object or array, the json_extract_scalar() function returns null. Use json_extract() for those cases.
  • Numeric and boolean values are returned as their string representations (e.g., 443, true). Use to_integer(), to_float(), or to_boolean() for type casting.
  • If the initial field is not already a JSON object, it must be converted using to_json_string() before extraction can occur.

json_extract_array()

The json_extract_array() function converts a JSON-formatted string into a native XQL array. Use this function to transform stringified lists into actionable data that can be indexed or filtered within your queries.

Constraint: The input field must be a valid JSON-formatted string. If the string is malformed or not in JSON format, the json_extract_array() function will return null. To convert a raw string field into JSON before extraction, use the to_json_string() function.

Canonical Syntax

Regular Syntax:

json_extract_array(field_name, "$.<json_path>")

Special characters syntax:

When a field in the JSON path contains reserved characters such as an at sign (@), dot (.), or colon (:), you must use bracket notation:

json_extract_array(field_name, "$[<json_path>]")

Parameters

Name Type Required Description
field_name string Yes The source field or expression containing a valid JSON-formatted string.
json_path string Yes A JSONPath expression pointing to a JSON array.

Return Value

The json_extract_array() function returns the extracted array as a native XQL array.

  • Full Array ([]): The json_extract_array() function returns an XQL-native array (e.g., ["22", "443"]).
  • Specific Element ([n]): The json_extract_array() function extracts the value at that index and returns it as a string (e.g., "22").

Important: Each element in the returned array retains surrounding double quotes when the array contains simple scalars. For example, string values are returned as "\"SSH\"" rather than SSH. Use json_extract_scalar_array() for clean unquoted values.

The json_extract_array() function returns null if:

  • The path does not exist.
  • The value at the path is not a JSON array.
  • The input is not valid JSON.

Examples

Example 1: Extract an array of objects and expand into rows

dataset = xdr_data
| alter users = json_extract_array(response_json, "$.users")
| arrayexpand users
| alter
    user_name = json_extract_scalar(users, "$.name"),
    user_role = json_extract_scalar(users, "$.role")

Input:

response_json
{"users":[{"name":"admin","role":"super"},{"name":"jdoe","role":"viewer"},{"name":"asmith","role":"editor"}]}

Output (after arrayexpand):

users user_name user_role
{"name":"admin","role":"super"} admin super
{"name":"jdoe","role":"viewer"} jdoe viewer
{"name":"asmith","role":"editor"} asmith editor

Example 2: Extract an array from a key containing @

dataset = xdr_data
| alter recipients = json_extract_array(email_data, "$[@recipients]")

Input:

email_data
{"@recipients":["user1@company.com","user2@company.com","user3@company.com"],"subject":"Alert"}

Output:

email_data recipients
{"@recipients":["user1@company.com","user2@company.com","user3@company.com"],"subject":"Alert"} ["\"user1@company.com\"","\"user2@company.com\"","\"user3@company.com\""]

Note: The output elements are doubly-quoted ("\"user1@company.com\"" instead of user1@company.com). For clean unquoted values, use json_extract_scalar_array() instead.

Syntactic Sugar alternative

For a more concise query, XQL supports a Syntactic Sugar format using the -> operator with [] suffix. This compiles to the exact same json_extract_array() engine but improves human readability for simple paths.

Syntactic Sugar format:

field_name -> <json_path>[]

Special characters Syntactic Sugar format:

field_name -> ["<json_path>"][]

Syntactic Sugar Example:

dataset = xdr_data
| alter users = response_json -> users[]
| arrayexpand users
| alter
    user_name = json_extract_scalar(users, "$.name"),
    user_role = json_extract_scalar(users, "$.role")

Notes

  • The returned value is a JSON array. Use XQL array functions such as array_length(), array_contains(), or arrayexpand to work with the result.
  • If the path points to a scalar or object (not an array), the json_extract_array() function returns null.
  • Combine with arrayexpand to flatten array elements into individual rows, then use json_extract_scalar() to extract fields from each element.
  • If the source data is a raw string, it must first be processed by to_json_string() before the array can be extracted.

json_extract_scalar_array()

The json_extract_scalar_array() function parses a string representing a JSON array and converts it into a native XQL array format. While functionally similar to json_extract_array(), this version extracts the elements as scalar values. This means the resulting array elements are returned in their raw form rather than being wrapped in JSON-style double quotes ("...").

Constraint: The input field must be a valid JSON-formatted string. If the string is malformed or not in JSON format, the json_extract_scalar_array() function will return null. To convert a raw string field into JSON before extraction, use the to_json_string() function.

Note: This function does not support Syntactic Sugar format. The function must always be called using the full regular function syntax.

Canonical Syntax

Regular Syntax:

json_extract_scalar_array(field_name, "$.<json_path>")

Special characters syntax:

When a field in the JSON path contains reserved characters such as an at sign (@), dot (.), or colon (:), you must use bracket notation:

json_extract_scalar_array(field_name, "$[<json_path>]")

Parameters

Name Type Required Description
field_name string Yes The source field or expression containing a valid JSON-formatted string.
json_path string Yes A JSONPath expression pointing to a JSON array of scalar values (strings, numbers, or booleans).

Return Value

The json_extract_scalar_array() function returns an array of plain strings with JSON quotes stripped from each element:

  • ["SSH", "HTTPS", "SNMP", "FTP"] → The json_extract_scalar_array() function returns SSH, HTTPS, SNMP, FTP
  • [443, 80, 8080] → The json_extract_scalar_array() function returns 443, 80, 8080
  • [true, false, true] → The json_extract_scalar_array() function returns true, false, true

The json_extract_scalar_array() function returns null if:

  • The path does not exist.
  • The value at the path is not a JSON array.
  • The input is not valid JSON.

Examples

Example 1: Extract a scalar array and filter by value

Note: This function does not support Syntactic Sugar format. Use the regular function syntax shown below.

dataset = xdr_data
| alter blocked_ips = json_extract_scalar_array(network_json, "$.blocked_ips")
| filter array_any(blocked_ips, "10.0.0.5")

Input:

network_json
{"blocked_ips":["10.0.0.5","192.168.1.1","172.16.0.10"]}
{"blocked_ips":["10.0.0.5","10.10.10.1"]}
{"blocked_ips":["192.168.1.1","172.16.0.10"]}

Output (after filter):

network_json blocked_ips
{"blocked_ips":["10.0.0.5","192.168.1.1","172.16.0.10"]} 10.0.0.5, 192.168.1.1, 172.16.0.10
{"blocked_ips":["10.0.0.5","10.10.10.1"]} 10.0.0.5, 10.10.10.1

Note: Because the values are clean strings (not doubly-quoted), array_contains(blocked_ips, "10.0.0.5") works directly without any trim() or replex() workarounds.

Example 2: Extract an array from a key containing @

dataset = xdr_data
| alter recipients = json_extract_scalar_array(email_data, "$[@recipients]")

Input:

email_data
{"@recipients":["user1@company.com","user2@company.com","user3@company.com"],"subject":"Alert"}

Output:

email_data recipients
{"@recipients":["user1@company.com","user2@company.com","user3@company.com"],"subject":"Alert"} user1@company.com, user2@company.com, user3@company.com

Note: Compare this with json_extract_array() which would return ["\"user1@company.com\"","\"user2@company.com\"","\"user3@company.com\""]. The json_extract_scalar_array() function returns clean, unquoted values.

Notes

  • The json_extract_scalar_array() function is the array equivalent of json_extract_scalar(): it strips JSON string quotes from each element in the array.
  • Best used when the JSON array contains only scalar values (strings, numbers, booleans). If the array contains objects or nested arrays, use json_extract_array() instead.
  • The result is compatible with XQL array functions: array_contains(), array_length(), arrayexpand, etc.
  • The json_extract_scalar_array() function does not support Syntactic Sugar format — it must always be called using the full function syntax.

Comparison Table

Function Returns Strips JSON Quotes Syntactic Sugar format Best For
json_extract() String (always) No field -> path{} Extracting any value while preserving JSON formatting
json_extract_scalar() Plain string (scalar only) Yes field -> path Extracting a single string/number/boolean for filters
json_extract_array() XQL array (elements doubly-quoted) No field -> path[] Extracting arrays (including arrays of objects)
json_extract_scalar_array() XQL array of plain strings Yes No Not supported Extracting arrays of scalar values for comparisons

Syntactic Sugar format (->) vs Regular Syntax — Performance and Behavior

Aspect Syntactic Sugar format (-> operator) Regular Syntax (json_extract*) Performance Difference
Underlying Engine Compiled to the same internal JSON extraction engine Same internal JSON extraction engine 🟢 No difference
Return Type Returns value directly (like json_extract_scalar for leaves) json_extract_scalar returns clean string; json_extract has quotes 🟢 No difference
Nested Access field -> nested.path.value json_extract_scalar(field, "$.nested.path.value") 🟢 No difference
Array Access field -> array[0] arrayindex(json_extract_array(field, "$.array"), 0) 🟢 No difference
Quote Handling Returns clean values — no surrounding quotes json_extract_scalar: clean; json_extract: has quotes 🟡 Sugar avoids quotes
Readability High — concise, natural dot notation Lower — verbose function calls with JSONPath strings N/A
Flexibility Limited — cannot use complex JSONPath (special chars, wildcards) Full JSONPath support including bracket notation, wildcards N/A
Use in arraymap Cannot use -> on @element json_extract_scalar("@element", "$.field") works N/A
Chaining field -> level1.level2.level3 (unlimited depth) Requires nested calls or complex JSONPath strings 🟢 No difference
Error Behavior Returns null for missing paths Returns null for missing paths 🟢 No difference
Filter Statements Cannot use -> in filter expressions reliably filter json_extract_scalar(field, "$.type") = "value" works N/A

When to use which syntax — Decision Guide

Scenario Recommended Syntax Reason Example
Simple leaf value from a known field Syntactic Sugar format (->) Cleaner, no quote issues, same performance event_data -> ProcessId
Key with @ or special characters json_extract_scalar() Syntactic Sugar format cannot handle special chars in keys json_extract_scalar(headers, "$[@from]")
Array index access (e.g., first element) json_extract_array() + arrayindex() Syntactic Sugar format has limited array index support arrayindex(json_extract_array(target, "$"), 0)
Iterating over array elements with arraymap json_extract_scalar() on @element Syntactic Sugar format cannot be used on @element arraymap(Entities, json_extract_scalar("@element", "$.hostName"))
Extracting a sub-object for further processing json_extract() Need to preserve JSON structure json_extract(winlog, "$.event_data")
Array of simple scalar values json_extract_scalar_array() Returns clean strings without quotes json_extract_scalar_array(user, "$.rolegrants")
Array of complex objects to iterate json_extract_array() Returns array for arraymap/arrayfilter json_extract_array(properties, "$.entities")
Filter/conditional expressions json_extract_scalar() More reliable in filter statements; no quote issues filter json_extract_scalar(details, "$.type") = "AUDIT"
Deep nested access on known structure Syntactic Sugar format (->) Much more readable resource -> data.placement.availabilityZone
Value that might be object/array/boolean json_extract() + tostring() json_extract_scalar returns null for non-leaf values tostring(json_extract(debugContext, "$.debugData.errorCode"))

Known limitations summary

# Limitation Affected Function Impact Workaround
1 String values include surrounding quotes json_extract() Breaks filter comparisons Use json_extract_scalar() for leaf values
2 Array elements retain quotes json_extract_array() Requires replex() workarounds Use json_extract_scalar_array() for scalar arrays
3 Requires to_json_string() conversion json_extract_scalar_array() Extra conversion step required Wrap input with to_json_string()
4 Cannot extract non-leaf values json_extract_scalar() Returns null for objects/arrays Use json_extract() then json_extract_scalar()
5 No reliable direct array index access json_extract_scalar() Requires 3-function chain Use json_extract_array() + arrayindex()
6 Cannot directly assign to XDM array fields json_extract_array() Quoted values in XDM fields Use json_extract_scalar_array() or arraymap()

JSONPath Quick Reference

Expression Description
$ Root element
$.key Child element named key
$.a.b Nested child: b inside a
$.array[0] First element of an array
$.array[*] All elements of an array
$.array[0].field Field field of the first array element
$[key] Bracket notation for keys with special characters
$['key.name'] Bracket notation with quotes for dotted keys
$[@field] Bracket notation for keys starting with @

Reference: Cortex XSIAM — XQL Language — JSON Functions

json_path_extract

Use the json_path_extract() function to retrieve values from a JSON string based on a specified JSONPath expression.

Syntax

json_path_extract (<json_field>, <json_path>)

Parameters

Name Type Required Description
json_field string Yes The field containing the JSON string from which to extract data.
json_path string Yes The JSONPath expression identifying the data to extract.

Returns

The json_path_extract() function returns a string representation of the extracted JSON data.

Usage notes

  • This function supports a syntactic sugar format for simplified query writing: <json_field> ->-> "<json_path>".
  • When using the regular syntax, the <json_path> must be enclosed in double quotes, and the root of the JSON object is represented by a dollar sign ($). The $ is not required when using the syntactic sugar format.
  • If a field name within the <json_path> contains special characters such as a dot (.) or colon (:), specific syntax is required:
  • Regular Syntax: Enclose the field name in single quotes within brackets (for example, ['<json_field>']).
  • Syntactic Sugar Format: Enclose the field name in double quotes within brackets (for example, ["<json_field>"]).
  • JSON field names are case-sensitive. The key-to-field pairing in the XQL query must be identical to the JSON structure for results to be found.
  • This function performs a "very heavy" operation and requires significant resources to run. Use it when standard dot-notation or simple array indexing is insufficient for your data extraction needs.
  • The syntax used corresponds to the standard found in the JavaScript jsonpath package.

Examples

Example 1: Extracting all authors from a nested array

Goal: Extract all author values from the ServerAccessConfig array within the Firewall object using the [*] wildcard.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 1
| alter json_field = "{\"Firewall\":{\"ServerAccessConfig\":[{\"category\":\"policy\",\"author\":\"NRees\",\"name\":\"CustomerSuccess_NoAccess\",\"priority\":8.95},{\"category\":\"rule\",\"author\":\"EWaugh\",\"name\":\"AllowAccess_10_10_10_10\",\"id\":\"0-553-21311-3\",\"priority\":12.99},{\"category\":\"policy\",\"author\":\"HMelville\",\"name\":\"SOC_Access\",\"priority\":8.99},{\"category\":\"rule\",\"author\":\"JTolkien\",\"name\":\"AllowAccess_JIT\",\"id\":\"0-395-19395-8\",\"priority\":22.99}],\"Reviewer\":{\"UserName\":\"jdow\",\"Role\":\"Admin\"}}}"
| alter all_authors = json_path_extract(json_field, "$.Firewall.ServerAccessConfig[*].author")
| fields event_id, all_authors

Explanation: The query defines a static JSON object and uses the [*] operator to iterate through all elements of the ServerAccessConfig array, extracting the author field from each.

Output:

EVENT_ID ALL_AUTHORS
101 ["NRees","EWaugh","HMelville","JTolkien"]

Example 2: Extracting authors with a specific priority

Goal: Use a filter expression within the JSONPath to find authors associated with a specific priority value.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 1
| alter json_field = "{\"Firewall\":{\"ServerAccessConfig\":[{\"category\":\"policy\",\"author\":\"NRees\",\"name\":\"CustomerSuccess_NoAccess\",\"priority\":8.95},{\"category\":\"rule\",\"author\":\"EWaugh\",\"name\":\"AllowAccess_10_10_10_10\",\"id\":\"0-553-21311-3\",\"priority\":12.99},{\"category\":\"policy\",\"author\":\"HMelville\",\"name\":\"SOC_Access\",\"priority\":8.99},{\"category\":\"rule\",\"author\":\"JTolkien\",\"name\":\"AllowAccess_JIT\",\"id\":\"0-395-19395-8\",\"priority\":22.99}],\"Reviewer\":{\"UserName\":\"jdow\",\"Role\":\"Admin\"}}}"
| alter specific_priority_author = json_path_extract(json_field, "$..*[?(@.priority==22.99)].author")
| fields event_id, specific_priority_author

Explanation: The query utilizes the filter expression ?(@.priority==22.99) to match objects where the priority is exactly 22.99, and then extracts the author from those matching objects.

Output:

EVENT_ID SPECIFIC_PRIORITY_AUTHOR
101 ["JTolkien"]

Example 3: Deep scan for all occurrences of a key

Goal: Use the recursive descent operator to find all author values anywhere in the JSON structure.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 1
| alter json_field = "{\"Firewall\":{\"ServerAccessConfig\":[{\"category\":\"policy\",\"author\":\"NRees\",\"name\":\"CustomerSuccess_NoAccess\",\"priority\":8.95},{\"category\":\"rule\",\"author\":\"EWaugh\",\"name\":\"AllowAccess_10_10_10_10\",\"id\":\"0-553-21311-3\",\"priority\":12.99},{\"category\":\"policy\",\"author\":\"HMelville\",\"name\":\"SOC_Access\",\"priority\":8.99},{\"category\":\"rule\",\"author\":\"JTolkien\",\"name\":\"AllowAccess_JIT\",\"id\":\"0-395-19395-8\",\"priority\":22.99}],\"Reviewer\":{\"UserName\":\"jdow\",\"Role\":\"Admin\"}}}"
| alter all_authors_deep_scan = json_path_extract(json_field, "$..author")
| fields event_id, all_authors_deep_scan

Explanation: The query uses the $..author syntax to perform a deep scan of the entire JSON object, locating and extracting the value of every key named author, regardless of its nesting level.

Output:

EVENT_ID ALL_AUTHORS_DEEP_SCAN
101 ["NRees","EWaugh","HMelville","JTolkien"]

Example 4: Extracting all direct properties of an object

Goal: Extract all direct properties (keys and values) of a specific object using the wildcard operator.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 1
| alter json_field = "{\"Firewall\":{\"ServerAccessConfig\":[{\"category\":\"policy\",\"author\":\"NRees\",\"name\":\"CustomerSuccess_NoAccess\",\"priority\":8.95},{\"category\":\"rule\",\"author\":\"EWaugh\",\"name\":\"AllowAccess_10_10_10_10\",\"id\":\"0-553-21311-3\",\"priority\":12.99},{\"category\":\"policy\",\"author\":\"HMelville\",\"name\":\"SOC_Access\",\"priority\":8.99},{\"category\":\"rule\",\"author\":\"JTolkien\",\"name\":\"AllowAccess_JIT\",\"id\":\"0-395-19395-8\",\"priority\":22.99}],\"Reviewer\":{\"UserName\":\"jdow\",\"Role\":\"Admin\"}}}"
| alter firewall_properties = json_path_extract(json_field, "$.Firewall.*")
| fields event_id, firewall_properties

Explanation: The query uses $.Firewall.* to retrieve all child elements (both the ServerAccessConfig array and the Reviewer object) directly under the Firewall key.

Output:

EVENT_ID FIREWALL_PROPERTIES
101 [[{"category": "policy","author": "NRees","name": "CustomerSuccess_NoAccess","priority": 8.95},{"category": "rule","author": "EWaugh","name": "AllowAccess_10_10_10_10","id": "0-553-21311-3","priority": 12.99},{"category": "policy","author": "HMelville","name": "SOC_Access","priority": 8.99},{"category": "rule","author": "JTolkien","name": "AllowAccess_JIT","id": "0-395-19395-8","priority": 22.99}],{"UserName": "jdow","Role": "Admin"}]

Example 5: Deep scan for specific keys

Goal: Use recursive descent to find all values associated with a specific key anywhere in the JSON.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 1
| alter json_field = "{\"Firewall\":{\"ServerAccessConfig\":[{\"category\":\"policy\",\"author\":\"NRees\",\"name\":\"CustomerSuccess_NoAccess\",\"priority\":8.95},{\"category\":\"rule\",\"author\":\"EWaugh\",\"name\":\"AllowAccess_10_10_10_10\",\"id\":\"0-553-21311-3\",\"priority\":12.99},{\"category\":\"policy\",\"author\":\"HMelville\",\"name\":\"SOC_Access\",\"priority\":8.99},{\"category\":\"rule\",\"author\":\"JTolkien\",\"name\":\"AllowAccess_JIT\",\"id\":\"0-395-19395-8\",\"priority\":22.99}],\"Reviewer\":{\"UserName\":\"jdow\",\"Role\":\"Admin\"}}}"
| alter all_priorities = json_path_extract(json_field, "$.Firewall..priority")
| fields event_id, all_priorities

Explanation: The query uses $.Firewall..priority to recursively scan within the Firewall object and extract every value associated with the key priority.

Output:

EVENT_ID ALL_PRIORITIES
101 [8.95,12.99,8.99,22.99]

Example 6: Extracting the last element of an array (length calculation)

Goal: Extract the last element of an array using the length property in a script expression.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 1
| alter json_field = "{\"Firewall\":{\"ServerAccessConfig\":[{\"category\":\"policy\",\"author\":\"NRees\",\"name\":\"CustomerSuccess_NoAccess\",\"priority\":8.95},{\"category\":\"rule\",\"author\":\"EWaugh\",\"name\":\"AllowAccess_10_10_10_10\",\"id\":\"0-553-21311-3\",\"priority\":12.99},{\"category\":\"policy\",\"author\":\"HMelville\",\"name\":\"SOC_Access\",\"priority\":8.99},{\"category\":\"rule\",\"author\":\"JTolkien\",\"name\":\"AllowAccess_JIT\",\"id\":\"0-395-19395-8\",\"priority\":22.99}],\"Reviewer\":{\"UserName\":\"jdow\",\"Role\":\"Admin\"}}}"
| alter last_access_config_item = json_path_extract(json_field, "$..ServerAccessConfig[(@.length-1)]")
| fields event_id, last_access_config_item

Explanation: The query uses the script expression (@.length-1) to calculate the index of the last element in the ServerAccessConfig array and extract it.

Output:

EVENT_ID LAST_ACCESS_CONFIG_ITEM
101 [{"category": "rule","author": "JTolkien","name": "AllowAccess_JIT","id": "0-395-19395-8","priority": 22.99}]

Example 7: Extracting the last element of an array (slice notation)

Goal: Extract the last element of an array using Python-style slice notation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 1
| alter json_field = "{\"Firewall\":{\"ServerAccessConfig\":[{\"category\":\"policy\",\"author\":\"NRees\",\"name\":\"CustomerSuccess_NoAccess\",\"priority\":8.95},{\"category\":\"rule\",\"author\":\"EWaugh\",\"name\":\"AllowAccess_10_10_10_10\",\"id\":\"0-553-21311-3\",\"priority\":12.99},{\"category\":\"policy\",\"author\":\"HMelville\",\"name\":\"SOC_Access\",\"priority\":8.99},{\"category\":\"rule\",\"author\":\"JTolkien\",\"name\":\"AllowAccess_JIT\",\"id\":\"0-395-19395-8\",\"priority\":22.99}],\"Reviewer\":{\"UserName\":\"jdow\",\"Role\":\"Admin\"}}}"
| alter last_access_config_slice = json_path_extract(json_field, "$..ServerAccessConfig[-1:]")
| fields event_id, last_access_config_slice

Explanation: The query uses the slice notation [-1:] to grab the last item in the ServerAccessConfig array.

Output:

EVENT_ID LAST_ACCESS_CONFIG_SLICE
101 [{"category": "rule","author": "JTolkien","name": "AllowAccess_JIT","id": "0-395-19395-8","priority": 22.99}]

Example 8: Extracting elements by specific indices

Goal: Extract multiple elements from an array by specifying their indices.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 1
| alter json_field = "{\"Firewall\":{\"ServerAccessConfig\":[{\"category\":\"policy\",\"author\":\"NRees\",\"name\":\"CustomerSuccess_NoAccess\",\"priority\":8.95},{\"category\":\"rule\",\"author\":\"EWaugh\",\"name\":\"AllowAccess_10_10_10_10\",\"id\":\"0-553-21311-3\",\"priority\":12.99},{\"category\":\"policy\",\"author\":\"HMelville\",\"name\":\"SOC_Access\",\"priority\":8.99},{\"category\":\"rule\",\"author\":\"JTolkien\",\"name\":\"AllowAccess_JIT\",\"id\":\"0-395-19395-8\",\"priority\":22.99}],\"Reviewer\":{\"UserName\":\"jdow\",\"Role\":\"Admin\"}}}"
| alter first_two_configs_by_index = json_path_extract(json_field, "$..ServerAccessConfig[0,1]")
| fields event_id, first_two_configs_by_index

Explanation: The query targets the ServerAccessConfig array and uses [0,1] to extract only the first and second elements.

Output:

EVENT_ID FIRST_TWO_CONFIGS_BY_INDEX
101 [{"category": "policy","author": "NRees","name": "CustomerSuccess_NoAccess","priority": 8.95},{"category": "rule","author": "EWaugh","name": "AllowAccess_10_10_10_10","id": "0-553-21311-3","priority": 12.99}]

Example 9: Extracting a slice from the beginning of an array

Goal: Extract a range of elements from the start of an array using slice notation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 1
| alter json_field = "{\"Firewall\":{\"ServerAccessConfig\":[{\"category\":\"policy\",\"author\":\"NRees\",\"name\":\"CustomerSuccess_NoAccess\",\"priority\":8.95},{\"category\":\"rule\",\"author\":\"EWaugh\",\"name\":\"AllowAccess_10_10_10_10\",\"id\":\"0-553-21311-3\",\"priority\":12.99},{\"category\":\"policy\",\"author\":\"HMelville\",\"name\":\"SOC_Access\",\"priority\":8.99},{\"category\":\"rule\",\"author\":\"JTolkien\",\"name\":\"AllowAccess_JIT\",\"id\":\"0-395-19395-8\",\"priority\":22.99}],\"Reviewer\":{\"UserName\":\"jdow\",\"Role\":\"Admin\"}}}"
| alter slice_from_beginning = json_path_extract(json_field, "$..ServerAccessConfig[:2]")
| fields event_id, slice_from_beginning

Explanation: The query uses [:2] to extract a slice of the ServerAccessConfig array starting from the beginning up to (but not including) index 2.

Output:

EVENT_ID SLICE_FROM_BEGINNING
101 [{"category": "policy","author": "NRees","name": "CustomerSuccess_NoAccess","priority": 8.95},{"category": "rule","author": "EWaugh","name": "AllowAccess_10_10_10_10","id": "0-553-21311-3","priority": 12.99}]

Example 10: Extract data using different JSONPath expressions

Goal: Define a JSON object called Firewall within a JSON field to illustrate multiple ways you can use the json_path_extract function to extract data using different JSONPath configurations.

XQL Code:

dataset = xdr_data | limit 1   
| alter  json_field = "{\"Firewall\": {\"ServerAccessConfig\": [{\"category\": \"policy\",\"author\": \"NRees\",\"name\": \"CustomerSuccess_NoAccess\",\"priority\": 8.95},{\"category\": \"rule\",\"author\": \"EWaugh\",\"name\": \"AllowAccess_10_10_10_10\",\"id\": \"0-553-21311-3\",\"priority\": 12.99},{\"category\": \"policy\",\"author\": \"HMelville\",\"name\": \"SOC_Access\",\"priority\": 8.99},{\"category\": \"rule\",\"author\": \"JTolkien\",\"name\": \"AllowAccess_JIT\",\"id\": \"0-395-19395-8\",\"priority\": 22.99}],\"Reviewer\": {\"UserName\": \"jdow\",\"Role\": \"Admin\"}}}"  
| alter a = json_path_extract(json_field, "$.Firewall.ServerAccessConfig[*].author")   
| alter b = json_path_extract(json_field, "$..*[?(@.priority==22.99)].author")   
| alter c = json_path_extract(json_field, "$..author")   
| alter d = json_path_extract(json_field, "$.Firewall.*")   
| alter e = json_path_extract(json_field, "$.Firewall..priority")   
| alter f = json_path_extract(json_field, "$..ServerAccessConfig[(@.length-1)]")   
| alter g = json_path_extract(json_field, "$..ServerAccessConfig[-1:]")   
| alter h = json_path_extract(json_field, "$..ServerAccessConfig[0,1]")   
| alter i = json_path_extract(json_field, "$..ServerAccessConfig[:2]")  
| fields json_field, a, b, c, d, e, f, g, h, i

Explanation: This query applies various JSONPath expressions to the json_field to extract different sets of data. Here is how the different fields are configured:

  • a: Outputs all of the values for the author key in the ServerAccessConfig JSON array.
  • b: Outputs all of the values for the author key, where the value for the priority key is 22.99.
  • c: Outputs all of the values for the author key anywhere found in the JSON.
  • d: Outputs all of the values under the Firewall key.
  • e: Outputs all of the values under the Firewall key for the priority key.
  • f: Outputs the JSON array index value from the ServerAccessConfig JSON array according to its index location from the end of the array.
  • g: Outputs all of the JSON array index values from the ServerAccessConfig JSON array according to its index location from the end of the array.
  • h: Outputs a specific set of JSON array index values (one or more) from the ServerAccessConfig JSON array according to its index location.
  • i: Outputs all of the JSON array index values from the ServerAccessConfig JSON array according to its index location from the start (0 Index) up to the mentioned index value.

Output:

_TIME JSON_FIELD A B C D
Jan 20th 2025 18:51:42 {"Firewall": {"ServerAccessConfig": [{"category": "policy", "author": "NRees", "name": "CustomerSuccess_NoAccess", "priority": 8.95}, ...], "Reviewer": {"UserName": "jdow", "Role": "Admin"}}} NRees, EWaugh, HMelville, JTolkien JTolkien NRees, EWaugh, HMelville, JTolkien {"ServerAccessConfig": [{"category": "policy", "author": "NRees", "name": "CustomerSuccess_NoAccess", "priority": 8.95}, ...], "Reviewer": {"UserName": "jdow", "Role": "Admin"}}

Note: Columns E through I, _PRODUCT, _VENDOR, and INSERT_TIMESTAMP are also returned by the query but omitted from this sample display for brevity.

lag

Use the lag() function to access data from a preceding row in the same result set, enabling comparisons between the current row and a previous one.

Syntax

lag(<field>, <offset>, <default_value>)

Parameters

Name Type Required Description
field any Yes The field from which to extract the value.
offset integer No The number of rows back from the current row from which to obtain a value. The default is 1.
default_value any No The value to return if the offset points to a row that does not exist (for example, before the first row of the partition). The default is NULL.

Returns

The lag() function returns the value of the specified field from the row that is the specified number of rows before the current row. The data type matches the data type of the input field.

Usage Notes

  • The lag() function is an analytic function and must be used within the windowcomp stage.
  • The chronological or logical order of the records is determined by the order by clause within the over() partition statement.
  • This function is frequently used to calculate differences between consecutive events, such as the time elapsed between logins or the change in file size between versions.

Examples

Example 1: Identify consecutive processes

Goal: Check the previous process name that was executed.

XQL Code:

dataset \= xdr_data
| filter action_process_image_name != null
| windowcomp lag(action_process_image_name) as previous_process over(partition by agent_hostname order by _time asc)
| fields agent_hostname, action_process_image_name, previous_process, _time
| sort asc agent_hostname , _time

Explanation: The windowcomp partition operates within the parameters defined in the over() partition statement. The query organizes the data to evaluate by agent_hostname according to the ascending chronological order of its creation time. The results returned in the new field previous_process provide the process image name of the previous event for each agent hostname. Because this calculation utilizes the windowcomp stage, all lines of the dataset are returned along with this new field value.

Output:

AGENT_HOSTNAME ACTION_PROCESS_IMAGE_NAME PREVIOUS_PROCESS _TIME
mac-agent-1 test-process-1 null Mar 26th 2026 09:26:07
mac-agent-1 test-process-2 test-process-1 Mar 26th 2026 09:26:08
mac-agent-1 test-process-3 test-process-2 Mar 26th 2026 09:26:09

last

Use the last() function to retrieve the last chronologically recorded value of a specified field within a grouped set of records.

Syntax

last (<field>)

Parameters

Name Type Required Description
field any Yes The field from which to extract the last recorded value based on chronological order.

Returns

The last() function returns the value of the specified field from the newest (most recent) event in the evaluated group. The data type of the returned value matches the data type of the input field.

Usage Notes

  • The last() function is an aggregate function and must be used within the comp stage.
  • The chronological order of the records is determined by the underlying time field (such as _time) associated with the data events.
  • If the last record contains a NULL value for the evaluated field, the function returns NULL.
  • This function is frequently used in threat hunting to identify the most recent occurrence of an artifact or behavior, such as the final command executed in a session, the last known IP address for a host, or the most recent process started by a specific user.

Examples

Example 1: Identify the last process executed per host

Goal: Find the most recently recorded process name that was executed on each endpoint within the queried time frame.

XQL Code:

config timeframe = 1d  
| dataset = xdr_data  
| filter action_process_image_name \!= null  
| comp last(action_process_image_name) as last_process by agent_hostname  
| fields agent_hostname, last_process  
| limit 3

Explanation: The comp stage groups the events by agent_hostname. For each unique host, the last() function evaluates the timeline of the records and returns the action_process_image_name associated with the chronologically newest event in that group. The result is assigned to the last_process alias.

Output:

AGENT_HOSTNAME LAST_PROCESS
endpoint-win-01 C:\Windows\System32\cmd.exe
endpoint-mac-02 /usr/libexec/xpcproxy
srv-linux-03 /bin/bash

Example 2: Retrieve the last command line for a process

Goal: Identify the final command line argument used to execute a specific process across your environment before the query timeframe ended.

XQL Code:

config timeframe = 1d  
| dataset = xdr_data  
| filter action_process_image_name = "powershell.exe"  
| comp last(action_process_image_command_line) as final_cmd by agent_hostname  
| fields agent_hostname, final_cmd  
| limit 2

Explanation: This query filters for execution events involving powershell.exe. Using the comp stage, it groups the results by agent_hostname and uses the last() function to extract the chronologically final command line argument executed by PowerShell on that host.

Output:

AGENT_HOSTNAME FINAL_CMD
endpoint-win-01 powershell.exe -WindowStyle Hidden -EncodedCommand JABz...
srv-win-02 "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -c "Get-Process"

last_value

Use the last_*value() function within the windowcomp stage to retrieve the value of a specified field from the last row within a defined window or partition. This function is useful when you need to access the most recent or final value of a sequence relative to the current row being processed.

Syntax

windowcomp last_value(\<field\>) [by \<field\> [,\<field\>,...]] sort [asc|desc] \<field1\> [, [asc|desc] \<field2\>,...] [between 0|null|\<number\>|-\<number\> [and 0|null|\<number\>|-\<number\>] [frame_type=range]] [as \<alias\>]  

Parameters

Name Type Required Description
field Any supported type Yes The field from which to retrieve the value.
by field string No Partitions the data into distinct groups. The last_value() function operates independently within each of these partitions.
sort field string Yes Defines the order of rows within each partition. This order is crucial for correctly identifying the "last" row for the last_value() calculation.
between clause string No Defines a "window frame" (a specific range of rows) relative to the current row. If omitted, the default window (typically the entire partition) is used.
alias string No Assigns an alias name to the new column displaying the result.

Returns

The last_value() function returns a single value that corresponds to the field's value in the last row within the defined window frame. This value is then returned for every row in that window, and its data type matches the data type of the input field.

Usage Notes

  • last_value() is a navigation function and must be used with the windowcomp stage.
  • The sort clause is mandatory because it defines the logical order of rows, determining which row is considered the "last" one.
  • If you use the by clause to partition the data, the function restarts its calculation for each new partition.
  • If the between clause is omitted, the default window frame typically covers the entire partition, meaning the "last" row is the last row of the partition.

Examples

Example 1: last_value() over an entire partition (default window)

Goal: Retrieve the duration_seconds from the last event within each is_successful group, ordered by _time, and replicate this value for every row in that group.

XQL Code:


config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, _time, is_successful, duration_seconds
| windowcomp last_value(duration_seconds) by is_successful sort asc _time as last_duration_in_partition
| sort asc is_successful, asc _time, asc event_id
| limit 10

Explanation: You partition the data by is_successful and sort it by _time ascending. For the false partition, the chronologically last event is 109 with duration_seconds = 0.05. For the true partition, the last event is 110 with duration_seconds = 60.0. These last values are assigned to all rows within their respective partitions.

Output:

_time event_id is_successful duration_seconds last_duration_in_partition
2023-10-26 10:05:30 UTC 102 false 0.8 0.05
2023-10-26 10:40:10 UTC 106 false 2.1 0.05
2023-10-26 10:55:55 UTC 109 false 0.05 0.05
2023-10-26 10:00:00 UTC 101 true 1.5 60.0
2023-10-26 10:15:15 UTC 103 true 10.2 60.0
2023-10-26 10:20:00 UTC 104 true 0.1 60.0
2023-10-26 10:30:45 UTC 105 true 5.0 60.0
2023-10-26 10:45:00 UTC 107 true 7.8 60.0
2023-10-26 10:50:20 UTC 108 true 15.3 60.0
2023-10-26 11:00:10 UTC 110 true 60.0 60.0

Example 2: last_value() with a rolling window

Goal: Retrieve the duration_seconds from the last event within a sliding window consisting of the current row and the two preceding rows.

XQL Code:


config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, _time, is_successful, duration_seconds
| windowcomp last_value(duration_seconds) by is_successful sort asc _time between -2 and 0 as rolling_last_value
| sort asc is_successful, asc _time, asc event_id
| limit 10

** Explanation** : You define a window of between -2 and 0 (the two preceding rows up to the current row). Because the current row is always the "last" element of this specific sliding window, the function simply returns the duration_seconds of the current row itself.

Output:

_time event_id is_successful duration_seconds rolling_last_value
2023-10-26 10:05:30 UTC 102 false 0.8 0.8
2023-10-26 10:40:10 UTC 106 false 2.1 2.1
2023-10-26 10:55:55 UTC 109 false 0.05 0.05
2023-10-26 10:00:00 UTC 101 true 1.5 1.5
2023-10-26 10:15:15 UTC 103 true 10.2 10.2
2023-10-26 10:20:00 UTC 104 true 0.1 0.1
2023-10-26 10:30:45 UTC 105 true 5.0 5.0
2023-10-26 10:45:00 UTC 107 true 7.8 7.8
2023-10-26 10:50:20 UTC 108 true 15.3 15.3
2023-10-26 11:00:10 UTC 110 true 60.0 60.0

Example 3: last_value() with a fixed window (current row to end)

Goal: Retrieve the duration_seconds from the last event in a window starting from the current row and extending to the end of the partition.

XQL Code:


config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, _time, is_successful, duration_seconds
| windowcomp last_value(duration_seconds) by is_successful sort asc _time between 0 and null as last_duration_from_current
| sort asc is_successful, asc _time, asc event_id
| limit 10

Explanation: You define a window of between 0 and null (current row to the end of the partition). For any given row, the "last" element within this window is always the last element of the entire partition. This produces the same result as Example 1.

Output:

_time event_id is_successful duration_seconds last_duration_from_current
2023-10-26 10:05:30 UTC 102 false 0.8 0.05
2023-10-26 10:40:10 UTC 106 false 2.1 0.05
2023-10-26 10:55:55 UTC 109 false 0.05 0.05
2023-10-26 10:00:00 UTC 101 true 1.5 60.0
2023-10-26 10:15:15 UTC 103 true 10.2 60.0
2023-10-26 10:20:00 UTC 104 true 0.1 60.0
2023-10-26 10:30:45 UTC 105 true 5.0 60.0
2023-10-26 10:45:00 UTC 107 true 7.8 60.0
2023-10-26 10:50:20 UTC 108 true 15.3 60.0
2023-10-26 11:00:10 UTC 110 true 60.0 60.0

latest

Use the latest() function within a comp or windowcomp stage to retrieve the single chronologically latest value for a specified field within each group of rows.

Syntax

comp latest(<field>) [as <alias>] [by <field1>[, <field2>...]] [addrawdata = true|false [as <target field>]]

Parameters

Name Type Required Description
field string, numeric, timestamp Yes The field from which you want to retrieve the chronologically latest value.
alias string No An optional name for the output column.
field1, field2 string, numeric, boolean No Optional fields used to group rows; the latest value is calculated independently for each group.
addrawdata boolean No When set to true, includes a column listing the raw events contributing to the aggregate result.

Returns

The latest() function returns a single value representing the chronologically latest entry for the specified field within its window or partition.

Usage Notes

  • This function requires the dataset to contain a time-related field (such as _time) to be considered valid for chronological evaluation.
  • When used with the comp stage, system fields like _time are removed from the result set unless explicitly included in the by clause.
  • The order of selection depends entirely on the chronological order of events.
  • If you utilize the addrawdata = true option, the query will process up to 50 defined fields and display up to 100 contributing events.

Examples

Example 1: Find the latest event ID across the entire dataset

Goal: Identify the ID of the most recent event recorded in the dataset.

XQL Code:

config timeframe = 1d  
| dataset = sample_xql_raw  
| comp latest(event_id) as latest_event_id

Explanation: This query scans the sample_xql_raw dataset for the last 24 hours and identifies the event_id of the record with the most recent timestamp.

Output:

LATEST_EVENT_ID
110

Example 2: Find the latest log entry grouped by success status

Goal: Retrieve the most recent raw log data separately for successful and unsuccessful events.

XQL Code:

config timeframe = 1d  
| dataset = sample_xql_raw  
| comp latest(raw_log_data) as latest_raw_log by is_successful

Explanation: The query partitions the data into groups based on the is_successful status and retrieves the chronologically latest raw_log_data for each group.

Output:

IS_SUCCESSFUL LATEST_RAW_LOG
true "Full backup of prod_db to S3 completed."
false "Client C2 hit rate limit on /data endpoint."

least

Use the least() function to return the smallest value from a list of expressions.

Syntax

least(<expression1>, <expression2> [, ...])

Parameters

Name Type Required Description
expression1 integer, float, string, timestamp Yes The first value to compare.
expression2 integer, float, string, timestamp Yes The second value to compare.
... integer, float, string, timestamp No Additional values to compare. Any number of additional expressions can be provided.

Returns

Type: Same as input type

Description: The least() function returns the smallest value among all the provided expressions. If any input is null, the function may skip null values or return null depending on the implementation.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Multiple Arguments: The function accepts two or more arguments and returns the minimum value among them.
  • Type Consistency: All arguments should be of compatible types for meaningful comparison.
  • Null Handling: If all inputs are null, the function returns null. If some inputs are null, the function typically returns the least non-null value.
  • String Comparison: When comparing strings, the function uses lexicographic (alphabetical) ordering.
  • Relationship to greatest(): The least() function is the counterpart of greatest(), which returns the largest value.
  • Common Use Cases: This function is typically used within the alter stage for selecting the minimum value across multiple fields, implementing upper bounds (capping values), and data normalization.

Examples

Example 1: Find least among literal values

Goal: Find the smallest value from a set of numeric literals.

XQL code:

dataset = xdr_data
| limit 1
| alter min_val = least(10, 25, 5, 42, 18)
| fields min_val

Explanation: The least() function compares all five literal values and returns 5, the smallest among them.

Output:

MIN_VAL
5

Example 2: Find least value across multiple fields

Goal: Find the smallest value among multiple numeric fields for each record.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter min_field = least(event_id, numeric_value, duration_seconds)
| fields event_id, numeric_value, duration_seconds, min_field
| limit 3

Explanation: This query compares the values of event_id, numeric_value, and duration_seconds for each record and returns the smallest value in min_field.

Output:

EVENT_ID NUMERIC_VALUE DURATION_SECONDS MIN_FIELD
101 5.0 1.5 1.5
102 200.0 0.8 0.8
103 50.0 10.2 10.2

Example 3: Implement an upper cap using least

Goal: Cap a value at a maximum threshold by using least() as an upper clamp.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter capped_duration = least(duration_seconds, 5.0)
| fields event_id, duration_seconds, capped_duration
| limit 3

Explanation: This query ensures that capped_duration does not exceed 5.0 by returning the lesser of duration_seconds and 5.0. Values above 5.0 are capped, while values below remain unchanged.

Output:

EVENT_ID DURATION_SECONDS CAPPED_DURATION
101 1.5 1.5
102 0.8 0.8
103 10.2 5.0

len

Use the len() function to return the number of characters contained in a specified string.

Syntax

len (<string>)

Parameters

Name Type Required Description
<string> string Yes The string field or literal you want to evaluate.

Returns

The len() function returns an integer representing the count of characters.

Usage notes

  • If the input string is empty (""), len() will return 0.
  • If a NULL value is passed to len(), the function will return NULL.
  • This function is typically used within the alter stage to create new fields or modify existing ones by calculating string lengths.
  • The function can also be used in filter stages to narrow down results based on string length.

Examples

Example 1: Calculating the length of an existing string field

Goal: Calculate the character count of an existing string field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter description_length = len(event_description)
| fields event_id, event_description, description_length
| limit 3

Explanation: This query creates a new field, description_length, by calculating the character count of each event_description.

Output:

EVENT_ID EVENT_DESCRIPTION DESCRIPTION_LENGTH
101 "User login successful" 23
102 "File access attempt" 20
103 "Network connection established" 30

Example 2: Calculating the length of a literal string

Goal: Calculate the length of a direct string literal provided in the query.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter literal_length = len("Palo Alto Networks")
| fields event_id, literal_length
| limit 3

Explanation: This query adds a new field, literal_length, which contains the constant length of the string "Palo Alto Networks" (18 characters, including spaces).

Output:

EVENT_ID LITERAL_LENGTH
101 18
102 18
103 18

Example 3: Calculating the length of a string derived from a numerical field

Goal: Calculate the length of a numerical field by first converting it to a string.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_id_string = to_string(event_id)
| alter id_string_length = len(event_id_string)
| fields event_id, event_id_string, id_string_length
| limit 3

Explanation: This query first converts the numerical event_id to its string representation (for example, 101 becomes "101") and then calculates the length of that string.

Output:

EVENT_ID EVENT_ID_STRING ID_STRING_LENGTH
101 "101" 3
102 "102" 3
103 "103" 3

Example 4: Calculating the length of a string extracted from JSON data

Goal: Extract a string value from a JSON field and calculate its length.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter status_string = simple_json_data -> status
| alter status_length = len(status_string)
| fields event_id, simple_json_data, status_string, status_length
| limit 3

Explanation: This query extracts the status value from simple_json_data as a string and then finds its length. For event_id 103, because simple_json_data does not contain a status key, status_string becomes NULL, and subsequently, status_length also becomes NULL.

Output:

EVENT_ID SIMPLE_JSON_DATA STATUS_STRING STATUS_LENGTH
101 {"status": "ok", "code": 200} "ok" 2
102 {"status": "fail", "error": "access_denied"} "fail" 4
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL NULL

Example 5: Handling empty strings and NULL values

Goal: Demonstrate the behavior of the function with empty strings and NULL inputs.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter empty_string_test = ""
| alter empty_string_length = len(empty_string_test)
| alter dst_domain_length = len(dst_domain)
| fields event_id, empty_string_test, empty_string_length, dst_domain, dst_domain_length
| limit 5

Explanation: For all events, empty_string_test is an empty string, so empty_string_length is 0. For event_id 105, dst_domain is NULL, resulting in dst_domain_length also being NULL. For other events with non-NULL dst_domain, their respective string lengths are calculated.

Output:

EVENT_ID EMPTY_STRING_TEST EMPTY_STRING_LENGTH DST_DOMAIN DST_DOMAIN_LENGTH
101 "" 0 "ec2.amazonaws.com" 19
102 "" 0 "sts.amazonaws.com" 19
103 "" 0 "www.google.com" 14
104 "" 0 "dropbox.com" 11
105 "" 0 NULL NULL

list (comp)

Use the list() function to collect all values of a specified field across grouped rows and return them as an array (list) within the comp stage. This is equivalent to ARRAY_AGG in SQL.

Syntax

| comp list(<field>) [by <group_field1>, <group_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
field any Yes The field whose values will be collected into a list.
group_field any No One or more fields to group the results by. If omitted, all rows are treated as a single group.
alias string No An alias for the output field. If not specified, the output field name defaults to list_<field>.

Returns

Type: array

Description: The list() function returns an array containing all values of the specified field within each group. NULL values may be included.

Usage notes

  • Duplicates: The list function collects all values, including duplicates.
  • Order: The order of elements in the resulting array is not guaranteed unless a sort stage is applied before the comp stage.
  • Distinct values: If you need only distinct values, consider using the values function instead.
  • Data types: The function can be used with any data type.
  • No grouping: When used without a by clause, all rows are aggregated into a single group.

Examples

Example 1: Collect all source IPs by destination IP

Goal: Collect all local IP addresses grouped by remote IP address.

XQL code:

dataset = xdr_data
| comp list(action_local_ip) by action_remote_ip as source_ips

Explanation: The list() function collects all action_local_ip values for each unique action_remote_ip and returns them as an array named source_ips.

Output:

ACTION_REMOTE_IP SOURCE_IPS
10.0.0.1 [192.168.1.10, 192.168.1.20, 192.168.1.10]
10.0.0.2 [192.168.1.30]

Example 2: Collect all usernames across all events

Goal: Collect all effective usernames from all events into a single array.

XQL code:

dataset = xdr_data
| comp list(actor_effective_username) as all_users

Explanation: Without a by clause, the list() function aggregates all actor_effective_username values across all rows into a single array named all_users.

Output:

ALL_USERS
[admin, user1, admin, user2, user1]

ln

Use the ln() function to calculate the natural logarithm (base e) of a numeric value.

Syntax

ln(<number>)

Parameters

Name Type Required Description
number integer, float Yes The numeric value for which to calculate the natural logarithm. The value must be greater than 0.

Returns

Type: float

Description: The ln() function returns the natural logarithm (base e) of the input value. If the input is null, zero, or negative, the function returns null or NaN.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The input number must be strictly greater than 0. The natural logarithm is undefined for zero and negative numbers.
  • Zero Input: ln(0) returns negative infinity or null.
  • Negative Input: ln(x) for x < 0 returns NaN or null.
  • Null Handling: If the input expression is null, the function returns null.
  • Identity Value: ln(1) always returns 0.0, and ln(e) returns 1.0.
  • Inverse Relationship: The ln() function is the inverse of exp(). That is, ln(exp(x)) = x and exp(ln(x)) = x for x > 0.
  • Common Use Cases: This function is typically used within the alter stage for logarithmic scaling, entropy calculations, growth rate analysis, and data normalization.

Examples

Example 1: Calculate natural logarithm of literal values

Goal: Calculate the natural logarithm for specific numeric literals to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = ln(1), result2 = ln(exp(1)), result3 = ln(10)
| fields result1, result2, result3

Explanation: ln(1) returns 0.0 (since e^0 = 1), ln(e) returns 1.0 (computed via ln(exp(1))), and ln(10) returns approximately 2.30259.

Output:

RESULT1 RESULT2 RESULT3
0.0 1.0 2.30259

Example 2: Calculate natural logarithm from a field value

Goal: Calculate the natural logarithm of values stored in a dataset field, filtering out non-positive values.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value > 0
| alter ln_result = ln(numeric_value)
| fields event_id, numeric_value, ln_result
| limit 3

Explanation: This query first filters the dataset to ensure numeric_value contains only positive values, then computes the natural logarithm for each value using ln() in the alter stage.

Output:

EVENT_ID NUMERIC_VALUE LN_RESULT
101 1.0 0.0
102 10.0 2.30259
103 100.0 4.60517

Example 3: Use natural logarithm for logarithmic scaling

Goal: Apply logarithmic scaling to large numeric values to compress the range for analysis.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id > 0
| alter log_scale_id = ln(event_id)
| fields event_id, log_scale_id
| limit 3

Explanation: This query applies ln() to the event_id field to create a logarithmically scaled version. This is useful for visualizing or analyzing data with a wide range of values.

Output:

EVENT_ID LOG_SCALE_ID
101 4.61512
102 4.62497
103 4.63473

log

Use the log() function to calculate the logarithm of a numeric value with a specified base.

Syntax

log(<number>, <base>)

Parameters

Name Type Required Description
number integer, float Yes The numeric value for which to calculate the logarithm. The value must be greater than 0.
base integer, float Yes The base of the logarithm. The value must be greater than 0 and not equal to 1.

Returns

Type: float

Description: The log() function returns the logarithm of the input value with the specified base. If either input is null, or if number is ≤ 0, or if base is ≤ 0 or equal to 1, the function returns null or NaN.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The number must be strictly greater than 0. The base must be strictly greater than 0 and not equal to 1.
  • Null Handling: If either input expression is null, the function returns null.
  • Special Cases: log(1, any_base) always returns 0.0. log(base, base) always returns 1.0.
  • Relationship to Other Logarithms: log(x, e) is equivalent to ln(x). log(x, 10) is equivalent to log10(x).
  • Change of Base: The function implements the change of base formula internally: log(x, b) = ln(x) / ln(b).
  • Common Use Cases: This function is typically used within the alter stage for logarithmic calculations with custom bases, information theory computations (base 2), and scientific analysis.

Examples

Example 1: Calculate logarithm with different bases

Goal: Calculate logarithms with various bases to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter log_base2 = log(8, 2), log_base10 = log(1000, 10), log_base3 = log(27, 3)
| fields log_base2, log_base10, log_base3

Explanation: log(8, 2) returns 3.0 (since 2^3 = 8), log(1000, 10) returns 3.0 (since 10^3 = 1000), and log(27, 3) returns 3.0 (since 3^3 = 27).

Output:

LOG_BASE2 LOG_BASE10 LOG_BASE3
3.0 3.0 3.0

Example 2: Calculate logarithm from a field value

Goal: Calculate the base-2 logarithm of values stored in a dataset field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value > 0
| alter log2_result = log(numeric_value, 2)
| fields event_id, numeric_value, log2_result
| limit 3

Explanation: This query computes the base-2 logarithm for each positive value in the numeric_value field. Base-2 logarithms are commonly used in computing to determine the number of bits needed to represent a value.

Output:

EVENT_ID NUMERIC_VALUE LOG2_RESULT
101 8.0 3.0
102 64.0 6.0
103 100.0 6.64386

Example 3: Use logarithm for information entropy calculation

Goal: Calculate the information content (in bits) of event probabilities using base-2 logarithm.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter duration_seconds > 0 and duration_seconds < 1
| alter info_content = multiply(-1, log(duration_seconds, 2))
| fields event_id, duration_seconds, info_content
| limit 3

Explanation: This query calculates the information content in bits using the formula -log2(p), where p is a probability-like value from duration_seconds. Higher information content indicates rarer events.

Output:

EVENT_ID DURATION_SECONDS INFO_CONTENT
101 0.5 1.0
102 0.25 2.0
103 0.8 0.32193

log10

Use the log10() function to calculate the base-10 (common) logarithm of a numeric value.

Syntax

log10(<number>)

Parameters

Name Type Required Description
number integer, float Yes The numeric value for which to calculate the base-10 logarithm. The value must be greater than 0.

Returns

Type: float

Description: The log10() function returns the base-10 logarithm of the input value. If the input is null, zero, or negative, the function returns null or NaN.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The input number must be strictly greater than 0. The base-10 logarithm is undefined for zero and negative numbers.
  • Zero Input: log10(0) returns negative infinity or null.
  • Negative Input: log10(x) for x < 0 returns NaN or null.
  • Null Handling: If the input expression is null, the function returns null.
  • Key Values: log10(1) returns 0.0, log10(10) returns 1.0, log10(100) returns 2.0, log10(1000) returns 3.0.
  • Relationship: log10(x) is equivalent to log(x, 10).
  • Common Use Cases: This function is typically used within the alter stage for decibel calculations, order-of-magnitude analysis, pH calculations, and logarithmic scaling of data.

Examples

Example 1: Calculate base-10 logarithm of literal values

Goal: Calculate the base-10 logarithm for specific numeric literals to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = log10(1), result2 = log10(100), result3 = log10(1000)
| fields result1, result2, result3

Explanation: log10(1) returns 0.0, log10(100) returns 2.0 (since 10^2 = 100), and log10(1000) returns 3.0 (since 10^3 = 1000).

Output:

RESULT1 RESULT2 RESULT3
0.0 2.0 3.0

Example 2: Calculate base-10 logarithm from a field value

Goal: Calculate the base-10 logarithm of values stored in a dataset field to determine their order of magnitude.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value > 0
| alter log10_result = log10(numeric_value)
| alter order_of_magnitude = floor(log10_result)
| fields event_id, numeric_value, log10_result, order_of_magnitude
| limit 3

Explanation: This query computes the base-10 logarithm for each positive value in numeric_value, then uses floor() to determine the order of magnitude. For example, a value of 500 has log10 ≈ 2.699, so its order of magnitude is 2 (hundreds).

Output:

EVENT_ID NUMERIC_VALUE LOG10_RESULT ORDER_OF_MAGNITUDE
101 5.0 0.69897 0
102 500.0 2.69897 2
103 10000.0 4.0 4

Example 3: Use log10 for decibel calculation

Goal: Calculate a decibel-like ratio using the base-10 logarithm.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value > 0 and duration_seconds > 0
| alter ratio = divide(numeric_value, duration_seconds)
| alter decibel_ratio = multiply(10, log10(ratio))
| fields event_id, numeric_value, duration_seconds, ratio, decibel_ratio
| limit 3

Explanation: This query calculates a decibel-like ratio using the formula 10 * log10(ratio), which is a common pattern in signal processing and audio engineering.

Output:

EVENT_ID NUMERIC_VALUE DURATION_SECONDS RATIO DECIBEL_RATIO
101 100.0 1.0 100.0 20.0
102 50.0 5.0 10.0 10.0
103 1000.0 10.0 100.0 20.0

lowercase

Use the lowercase() function to convert all characters in a specified string field or literal to their corresponding lowercase representation.

Syntax

lowercase (<string>)

Parameters

Name Type Required Description
string string Yes The input string field or literal value to be converted to lowercase.

Returns

The lowercase() function returns a string where all characters of the input have been converted to lowercase.

Usage notes

  • The function requires a string input.
  • If a NULL value is passed to the function, it returns NULL.
  • This function is typically used within the alter stage to create new fields or modify existing ones.
  • The function can also be directly used within filter stages to enable case-insensitive comparisons without setting config case_sensitive = false globally.

Examples

Example 1: Lowercasing an existing string field

Goal: Convert the event_description field to all lowercase letters.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter lower_description = lowercase(event_description)
| fields event_id, event_description, lower_description
| limit 3

Explanation: This query creates a new field, lower_description, containing the lowercase version of each event_description.

Output:

EVENT_ID EVENT_DESCRIPTION LOWER_DESCRIPTION
101 "User login successful" "user login successful"
102 "File access attempt" "file access attempt"
103 "Network connection established" "network connection established"

Example 2: Lowercasing a literal string

Goal: Convert a direct string literal with mixed case to lowercase.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter lower_literal = lowercase("PaLo AlTo NeTwOrKs")
| fields event_id, lower_literal
| limit 3

Explanation: This query adds a new field, lower_literal, which contains the constant lowercase string "palo alto networks" for each record.

Output:

EVENT_ID LOWER_LITERAL
101 "palo alto networks"
102 "palo alto networks"
103 "palo alto networks"

Example 3: Lowercasing a string extracted from JSON data

Goal: Extract a string value from a JSON field and convert it to lowercase.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter status_string = simple_json_data -> status
| alter lower_json_status = lowercase(status_string)
| fields event_id, simple_json_data, status_string, lower_json_status
| limit 3

Explanation: This query extracts the status value from simple_json_data as a string and then converts it to lowercase. For event_id 103, because simple_json_data does not contain a status key, status_string becomes NULL, and subsequently, lower_json_status also becomes NULL.

Output:

EVENT_ID SIMPLE_JSON_DATA STATUS_STRING LOWER_JSON_STATUS
101 {"status": "ok", "code": 200} "ok" "ok"
102 {"status": "fail", "error": "access_denied"} "fail" "fail"
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL NULL

Example 4: Lowercasing a numerical field after explicit string conversion

Goal: Convert a numerical field to a string and then attempt to lowercase it.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_id_string = to_string(event_id)
| alter lower_id_string = lowercase(event_id_string)
| fields event_id, event_id_string, lower_id_string
| limit 3

Explanation: This query first converts the numerical event_id to its string representation (for example, 101 becomes "101") and then attempts to lowercase it. Because numeric strings only contain digits, the lowercase() function does not alter them.

Output:

EVENT_ID EVENT_ID_STRING LOWER_ID_STRING
101 "101" "101"
102 "102" "102"
103 "103" "103"

Example 5: Using lowercase() within a filter stage for case-insensitive matching

Goal: Filter for events containing a specific word, ignoring case sensitivity.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter lowercase(event_description) contains "user"
| fields event_id, event_description
| limit 3

Explanation: This query applies lowercase() to the event_description field for each record and then checks if the resulting lowercase string contains the substring "user". This allows it to match "User login successful" even though the original string has an uppercase 'U'.

Output:

EVENT_ID EVENT_DESCRIPTION
101 "User login successful"

Example: Normalize and deduplicate process names

Goal: Convert all actor_process_image_name field values that are not null to lowercase and return a list of unique values.

XQL Code:

dataset = ample_xql_raw 
| fields actor_process_image_name as apin 
| dedup apin by asc _time 
| filter apin != null 
| alter apin = lowercase(apin)

Explanation: The query begins by selecting the actor_process_image_name field from the xample_xql_raw dataset and aliasing it as apin.

  • The dedup stage is used to keep only unique occurrences of each process name, specifically retaining the first occurrence based on ascending time.
  • The filter stage removes any records where the process name is null.
  • Finally, the alter stage applies the lowercase() function to the apin field, standardizing all unique process names into lowercase format for consistent reporting and analysis.

Output:

apin
chrome.exe
svchost.exe
terminal
notepad.exe

ltrim

Use the ltrim() function to remove specified characters or whitespace from the beginning (left side) of a string.

Syntax

ltrim (<string>, [trim_characters])

Parameters

Name Type Required Description
string string Yes The string field or literal value you wish to modify.
trim_characters string No A string containing the set of characters to remove from the left side of the string. If omitted, defaults to whitespace characters (spaces and tabs).

Returns

The ltrim() function returns a new string with the specified leading characters removed.

Usage notes

  • The trim_characters parameter is treated as a set of individual characters. Any character from this set found at the beginning of the string will be removed repeatedly until a character that is not in the set is encountered.
  • If the trim_characters parameter is not specified, the function defaults to removing leading whitespace characters (spaces and tabs).
  • If a NULL value is passed as the input string, the function returns NULL.

Examples

Example 1: Removing leading whitespace (default behavior)

Goal: Remove leading spaces from a literal string by relying on the function's default behavior.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter trimmed_text = ltrim("   Important Message") 
| fields event_id, trimmed_text 
| limit 3

Explanation: This query creates a new field, trimmed_text, where the leading spaces from the literal string " Important Message" are removed, resulting in "Important Message".

Output:

EVENT_ID TRIMMED_TEXT
101 Important Message
102 Important Message
103 Important Message

Example 2: Removing a specific exact prefix

Goal: Remove the exact "www." prefix from the dst_domain field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter trimmed_domain = ltrim(dst_domain, "www.") 
| fields event_id, dst_domain, trimmed_domain 
| limit 3

Explanation: For event_id 103, the leading "www." from "www.google.com" is removed, leaving "google.com". For other records where the trim_characters ("www.") are not found at the very beginning, the string remains unchanged.

Output:

EVENT_ID DST_DOMAIN TRIMMED_DOMAIN
101 ec2.amazonaws.com ec2.amazonaws.com
102 sts.amazonaws.com sts.amazonaws.com
103 www.google.com google.com

Example 3: Removing specific leading characters (character set removal)

Goal: Remove a set of characters ('U', 'e', 'o', 'r', space) from the beginning of the raw_log_data field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter processed_log = ltrim(raw_log_data, "Ueor ") 
| fields event_id, raw_log_data, processed_log 
| limit 3

Explanation: For event_id 101, the log starts with "User Alice...". 'U' is in the set "Ueor ", so it is removed. The next character 's' is not in the set, so the process stops. The result is "ser Alice logged in from 192.168.1.10".

Output:

EVENT_ID RAW_LOG_DATA PROCESSED_LOG
101 User Alice logged in from 192.168.1.10 ser Alice logged in from 192.168.1.10
102 Process cmd.exe attempted to access /etc/passwd Process cmd.exe attempted to access /etc/passwd
103 Outbound connection to 1.1.1.1:443 initiated by AppX Outbound connection to 1.1.1.1:443 initiated by AppX

Example 4: ltrim() on a string extracted from JSON data

Goal: Remove specific characters ('f' or 'o') from the beginning of a string extracted from a JSON field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter status_string = simple_json_data -> status 
| alter trimmed_json_status = ltrim(status_string, "fo") 
| fields event_id, simple_json_data, status_string, trimmed_json_status 
| limit 3

Explanation: For event_id 101, "ok" becomes "k" because 'o' is removed. For event_id 102, "fail" becomes "ail" because 'f' is removed. For event_id 103, status_string is NULL, so trimmed_json_status is also NULL.

Output:

EVENT_ID SIMPLE_JSON_DATA STATUS_STRING TRIMMED_JSON_STATUS
101 {"status": "ok", "code": 200} ok k
102 {"status": "fail", "error": "access_denied"} fail ail
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL NULL

Example 5: ltrim() on a numerical field after explicit string conversion

Goal: Remove specific digits ('1' or '0') from the beginning of the event_id field after converting it to a string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_id_string = to_string(event_id) 
| alter trimmed_id_string = ltrim(event_id_string, "10") 
| fields event_id, event_id_string, trimmed_id_string 
| limit 3

Explanation: For event_id 101 ("101"), '1', then '0', then '1' are all removed because they are in the set "10", resulting in an empty string. For event_id 102 ("102"), '1' and '0' are removed, leaving "2". For event_id 103 ("103"), '1' and '0' are removed, leaving "3".

Output:

EVENT_ID EVENT_ID_STRING TRIMMED_ID_STRING
101 101  
102 102 2
103 103 3

Example 6: ltrim() on a field with no matching leading characters

Goal: Attempt to remove characters ('x', 'y', 'z') that do not exist at the start of the string field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter no_change_desc = ltrim(event_description, "xyz") 
| fields event_id, event_description, no_change_desc 
| limit 3

Explanation: Because none of the characters 'x', 'y', or 'z' are present at the beginning of "User login successful" or other event_description values, the function returns the original string unchanged.

Output:

EVENT_ID EVENT_DESCRIPTION NO_CHANGE_DESC
101 User login successful User login successful
102 File access attempt File access attempt
103 Network connection established Network connection established

max (comp)

Use the max() function to return the maximum value of a specified field across all rows in each group within the comp stage. This is equivalent to the SQL MAX aggregate function.

Syntax

| comp max(<field>) [by <group_field1>, <group_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
field numeric, string, or datetime Yes The field from which to find the maximum value.
group_field any No One or more fields to group the results by. If omitted, all rows are treated as a single group.
alias string No An alias for the output field. If not specified, the output field name defaults to max_<field>.

Returns

Type: same as input field

Description: The max() function returns the maximum value found in the specified field within each group. Returns NULL if all values in the group are NULL.

Usage notes

  • Data types: The max function works with numeric, string, and datetime fields.
  • String comparison: For string fields, the maximum is determined by lexicographic (alphabetical) ordering.
  • Null handling: NULL values are ignored in the computation.
  • No grouping: When used without a by clause, the function returns the maximum value across all rows.
  • Multiple aggregations: Can be combined with other aggregation functions in the same comp stage.

Examples

Example 1: Find the latest event time per host

Goal: Find the most recent event timestamp for each host.

XQL code:

dataset = xdr_data
| comp max(_time) by agent_hostname as latest_event

Explanation: The max() function finds the latest _time value for each unique agent_hostname, returning the most recent event time per host.

Output:

AGENT_HOSTNAME LATEST_EVENT
workstation-1 2024-01-15 14:30:00
workstation-2 2024-01-15 13:45:00

Example 2: Find the maximum severity across all alerts

Goal: Find the highest alert severity value across all events.

XQL code:

dataset = xdr_data
| comp max(alert_severity) as highest_severity

Explanation: Without a by clause, the max() function scans all rows and returns the single highest alert_severity value.

Output:

HIGHEST_SEVERITY
10

Example 3: Multiple aggregations in one comp stage

Goal: Find both the latest and earliest event times per host in a single query.

XQL code:

dataset = xdr_data
| comp max(_time) as latest, min(_time) as earliest by agent_hostname

Explanation: This query combines max() and min() in a single comp stage to find both the latest and earliest event timestamps for each agent_hostname.

Output:

AGENT_HOSTNAME LATEST EARLIEST
workstation-1 2024-01-15 14:30:00 2024-01-15 08:00:00

max (windowcomp)

Use the max() function within the windowcomp stage to compute the maximum value of a specified field over a window of rows. Unlike the comp stage version, the windowcomp version preserves all original rows and adds the computed maximum as a new field. This is equivalent to MAX() OVER(...) in SQL.

Syntax

| windowcomp max(<field>) [by <partition_field1>, <partition_field2>, ...] [sort [asc|desc] <sort_field1>, ...] [between <lower> [and <upper>] [frame_type=rows|range]] [as <alias>]

Parameters

Name Type Required Description
field numeric, string, or datetime Yes The field from which to compute the maximum value.
partition_field any No One or more fields to partition the data by (equivalent to SQL PARTITION BY).
sort_field any No One or more fields to define the order within each partition (equivalent to SQL ORDER BY). Defaults to ascending.
lower integer or null No Lower bound of the window frame. 0 = current row, negative = rows before, null = unbounded.
upper integer or null No Upper bound of the window frame. 0 = current row, positive = rows after, null = unbounded.
frame_type rows or range No Type of window frame. rows (default) uses physical row offsets; range uses value-based offsets on the sort field.
alias string No An alias for the output field.

Returns

Type: same as input field

Description: The max() function returns the maximum value within the defined window for each row. All original rows are preserved.

Usage notes

  • Row preservation: Unlike comp max(), the windowcomp max() does not reduce the number of rows. Each row retains its original data and gets an additional field with the window maximum.
  • No partitioning: If no by clause is specified, the window spans the entire result set.
  • Default frame: If no window frame is specified, the default frame is from the start of the partition to the current row (between null and 0).
  • Null handling: NULL values are ignored in the computation.
  • Running maximum: Can be combined with sort to compute running maximums.

Examples

Example 1: Maximum severity per host (all rows preserved)

Goal: Compute the maximum alert severity for each host while preserving all original rows.

XQL code:

dataset = xdr_data
| windowcomp max(alert_severity) by agent_hostname as max_severity_per_host

Explanation: The max() function computes the maximum alert_severity within each agent_hostname partition. All original rows are preserved, and each row receives the partition's maximum value in max_severity_per_host.

Output:

_TIME AGENT_HOSTNAME ALERT_SEVERITY MAX_SEVERITY_PER_HOST
2024-01-15 08:00:00 workstation-1 3 7
2024-01-15 09:00:00 workstation-1 7 7
2024-01-15 10:00:00 workstation-1 5 7
2024-01-15 08:30:00 workstation-2 2 4
2024-01-15 09:30:00 workstation-2 4 4

Example 2: Running maximum over time

Goal: Compute a running maximum of bytes sent, ordered by time.

XQL code:

dataset = xdr_data
| windowcomp max(bytes_sent) sort asc _time as running_max_bytes

Explanation: By sorting on _time in ascending order, the max() function computes a running maximum of bytes_sent from the start of the dataset up to the current row.

Output:

_TIME BYTES_SENT RUNNING_MAX_BYTES
2024-01-15 08:00:00 100 100
2024-01-15 09:00:00 250 250
2024-01-15 10:00:00 150 250
2024-01-15 11:00:00 300 300

Example 3: Maximum within a sliding window of 3 rows

Goal: Compute the maximum bytes sent within a sliding window of 3 rows (current row plus one row before and one row after).

XQL code:

dataset = xdr_data
| windowcomp max(bytes_sent) sort asc _time between -1 and 1 as local_max

Explanation: The between -1 and 1 clause defines a sliding window of 3 rows centered on the current row. The max() function returns the maximum bytes_sent value within that window for each row.

Output:

_TIME BYTES_SENT LOCAL_MAX
2024-01-15 08:00:00 100 250
2024-01-15 09:00:00 250 250
2024-01-15 10:00:00 150 300
2024-01-15 11:00:00 300 300

md5

Use the md5() function to compute the MD5 (Message-Digest Algorithm 5) hash of an input string.

Syntax

md5 ("<input_string>")

Parameters

Name Type Required Description
input_string string Yes The string to be hashed.

Returns

The md5() function returns the MD5 hash value as a string.

Usage notes

  • The function strictly requires a single string input.
  • MD5 is a one-way cryptographic hash function, meaning it is computationally infeasible to reverse the hashing process to obtain the original string from its hash.

Examples

Example 1: Hashing a basic literal string

Goal: Compute the MD5 hash of a simple literal string.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_string = md5("Hello world")
| fields event_id, hashed_string
| limit 3

Explanation: This query adds a new field, hashed_string, which contains the MD5 hash of "Hello world" for each record.

Output:

EVENT_ID HASHED_STRING
101 3e25960a79dbc69b674cd4ec67a72c65
102 3e25960a79dbc69b674cd4ec67a72c65
103 3e25960a79dbc69b674cd4ec67a72c65

Example 2: Hashing an existing string field

Goal: Hash values from an existing string field from the dataset.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_description = md5(event_description)
| fields event_id, event_description, hashed_description
| limit 3

Explanation: The query creates hashed_description by applying the md5() function to the event_description for each record.

Output:

EVENT_ID EVENT_DESCRIPTION HASHED_DESCRIPTION
101 User login successful e7751c911ee0541703e8787729227f2f
102 File access attempt be2254e015ee6b158055c56c2e28a506
103 Network connection established 24b74f0c43666f272a85e13b06385a86

Example 3: Hashing a string derived from a non-string field

Goal: Convert a numeric field to a string and then hash it.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_id_string = to_string(event_id)
| alter hashed_id = md5(event_id_string)
| fields event_id, event_id_string, hashed_id
| limit 3

Explanation: The event_id is first converted to a string, and then md5() hashes this string representation.

Output:

EVENT_ID EVENT_ID_STRING HASHED_ID
101 101 b16f6b0f199be0c27271457193f5509a
102 102 00d720d2a8b233c4424316d764491024
103 103 19842c38f4d9b40778c1a9386d3d4b68

Example 4: Hashing an empty string

Goal: Show the consistent result of hashing an empty string.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_empty_string = md5("")
| fields event_id, hashed_empty_string
| limit 3

Explanation: Hashing an empty string consistently results in the well-known MD5 hash for an empty string.

Output:

EVENT_ID HASHED_EMPTY_STRING
101 d41d8cd98f00b204e9800998ecf8427e
102 d41d8cd98f00b204e9800998ecf8427e
103 d41d8cd98f00b204e9800998ecf8427e

Example 5: Handling NULL input

Goal: Illustrate the function's behavior when provided with a NULL input.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_null_field = md5(dst_domain)
| fields event_id, dst_domain, hashed_null_field
| limit 5

Explanation: When the input to md5() is NULL, the function consistently returns NULL for the output field.

Output:

EVENT_ID DST_DOMAIN HASHED_NULL_FIELD
101 ec2.amazonaws.com 7f2f11181827457c134012019c4d93e5
102 sts.amazonaws.com fdf15a896d92008779b1248039601d0f
103 www.google.com 121f66cb71158f964092b3a1a36173a7
104 dropbox.com 84b7a137e0c4573130d740a6b47c0b6b
105 NULL NULL

median (comp)

Use the median() function to return the median (middle value) of a specified numeric field across all rows in each group within the comp stage. For an even number of values, it returns the average of the two middle values. This is equivalent to computing the 50th percentile.

Syntax

| comp median(<field>) [by <group_field1>, <group_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
field numeric Yes The numeric field from which to compute the median value.
group_field any No One or more fields to group the results by. If omitted, all rows are treated as a single group.
alias string No An alias for the output field. If not specified, the output field name defaults to median_<field>.

Returns

Type: numeric (float)

Description: The median() function returns the median value of the specified field within each group. Returns NULL if all values in the group are NULL.

Usage notes

  • Numeric only: The median function only works with numeric fields.
  • Null handling: NULL values are ignored in the computation.
  • Odd count: For an odd number of non-NULL values, the median is the middle value when sorted.
  • Even count: For an even number of non-NULL values, the median is the average of the two middle values.
  • Return type: The result is always returned as a floating-point number.
  • No grouping: When used without a by clause, the function computes the median across all rows.

Examples

Example 1: Median response time per endpoint

Goal: Compute the median response time for each host.

XQL code:

dataset = xdr_data
| comp median(action_total_time) by agent_hostname as median_response_time

Explanation: The median() function computes the middle value of action_total_time for each unique agent_hostname, providing a representative response time that is less affected by outliers than the average.

Output:

AGENT_HOSTNAME MEDIAN_RESPONSE_TIME
workstation-1 45.5
workstation-2 32.0

Example 2: Overall median of bytes received

Goal: Compute the overall median of bytes received across all events.

XQL code:

dataset = xdr_data
| comp median(action_network_bytes_received) as median_bytes

Explanation: Without a by clause, the median() function computes the median of action_network_bytes_received across all rows, returning a single value.

Output:

MEDIAN_BYTES
1024.5

median (windowcomp)

Use the median() function within the windowcomp stage to compute the median value of a specified numeric field over a window of rows. Unlike the comp stage version, the windowcomp version preserves all original rows and adds the computed median as a new field. This is equivalent to PERCENTILE_CONT(0.5) OVER(...) in SQL.

Syntax

| windowcomp median(<field>) [by <partition_field1>, <partition_field2>, ...] [sort [asc|desc] <sort_field1>, ...] [between <lower> [and <upper>] [frame_type=rows|range]] [as <alias>]

Parameters

Name Type Required Description
field numeric Yes The numeric field from which to compute the median value.
partition_field any No One or more fields to partition the data by (equivalent to SQL PARTITION BY).
sort_field any No One or more fields to define the order within each partition (equivalent to SQL ORDER BY). Defaults to ascending.
lower integer or null No Lower bound of the window frame. 0 = current row, negative = rows before, null = unbounded.
upper integer or null No Upper bound of the window frame. 0 = current row, positive = rows after, null = unbounded.
frame_type rows or range No Type of window frame. rows (default) uses physical row offsets; range uses value-based offsets on the sort field.
alias string No An alias for the output field.

Returns

Type: numeric (float)

Description: The median() function returns the median value within the defined window for each row. All original rows are preserved.

Usage notes

  • Row preservation: Unlike comp median(), the windowcomp median() does not reduce the number of rows.
  • Percentile: The median is the 50th percentile of the values within the window.
  • Null handling: NULL values are ignored in the computation.
  • Default frame: If no window frame is specified, the default frame is from the start of the partition to the current row (between null and 0).
  • Trend analysis: Useful for computing running medians or sliding window medians for trend analysis.

Examples

Example 1: Median bytes per host (all rows preserved)

Goal: Compute the median bytes received for each host while preserving all original rows.

XQL code:

dataset = xdr_data
| windowcomp median(action_network_bytes_received) by agent_hostname as median_bytes_per_host

Explanation: The median() function computes the median of action_network_bytes_received within each agent_hostname partition. All original rows are preserved, and each row receives the partition's median value in median_bytes_per_host.

Output:

_TIME AGENT_HOSTNAME ACTION_NETWORK_BYTES_RECEIVED MEDIAN_BYTES_PER_HOST
2024-01-15 08:00:00 workstation-1 100 200.0
2024-01-15 09:00:00 workstation-1 300 200.0
2024-01-15 10:00:00 workstation-1 200 200.0
2024-01-15 08:30:00 workstation-2 500 400.0
2024-01-15 09:30:00 workstation-2 300 400.0

Example 2: Running median of response times

Goal: Compute a running median of response times, ordered by time.

XQL code:

dataset = xdr_data
| windowcomp median(action_total_time) sort asc _time as running_median

Explanation: By sorting on _time in ascending order, the median() function computes a running median of action_total_time from the start of the dataset up to the current row.

Output:

_TIME ACTION_TOTAL_TIME RUNNING_MEDIAN
2024-01-15 08:00:00 50 50.0
2024-01-15 09:00:00 30 40.0
2024-01-15 10:00:00 70 50.0
2024-01-15 11:00:00 40 45.0

min (comp)

Use the min() function to return the minimum value of a specified field across all rows in each group within the comp stage. This is equivalent to the SQL MIN aggregate function.

Syntax

| comp min(<field>) [by <group_field1>, <group_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
field numeric, string, or datetime Yes The field from which to find the minimum value.
group_field any No One or more fields to group the results by. If omitted, all rows are treated as a single group.
alias string No An alias for the output field. If not specified, the output field name defaults to min_<field>.

Returns

Type: same as input field

Description: The min() function returns the minimum value found in the specified field within each group. Returns NULL if all values in the group are NULL.

Usage notes

  • Data types: The min function works with numeric, string, and datetime fields.
  • String comparison: For string fields, the minimum is determined by lexicographic (alphabetical) ordering.
  • Null handling: NULL values are ignored in the computation.
  • No grouping: When used without a by clause, the function returns the minimum value across all rows.
  • Multiple aggregations: Can be combined with other aggregation functions in the same comp stage.

Examples

Example 1: Find the earliest event time per host

Goal: Find the earliest event timestamp for each host.

XQL code:

dataset = xdr_data
| comp min(_time) by agent_hostname as earliest_event

Explanation: The min() function finds the earliest _time value for each unique agent_hostname, returning the first event time per host.

Output:

AGENT_HOSTNAME EARLIEST_EVENT
workstation-1 2024-01-15 08:00:00
workstation-2 2024-01-15 08:30:00

Example 2: Find the minimum severity across all alerts

Goal: Find the lowest alert severity value across all events.

XQL code:

dataset = xdr_data
| comp min(alert_severity) as lowest_severity

Explanation: Without a by clause, the min() function scans all rows and returns the single lowest alert_severity value.

Output:

LOWEST_SEVERITY
1

Example 3: Multiple aggregations in one comp stage

Goal: Find both the earliest and latest event times, plus total count, per host in a single query.

XQL code:

dataset = xdr_data
| comp min(_time) as earliest, max(_time) as latest, count(*) as total by agent_hostname

Explanation: This query combines min(), max(), and count() in a single comp stage to find the earliest and latest event timestamps along with the total event count for each agent_hostname.

Output:

AGENT_HOSTNAME EARLIEST LATEST TOTAL
workstation-1 2024-01-15 08:00:00 2024-01-15 14:30:00 25
workstation-2 2024-01-15 08:30:00 2024-01-15 13:45:00 18

min (windowcomp)

Use the min() function within the windowcomp stage to compute the minimum value of a specified field over a window of rows. Unlike the comp stage version, the windowcomp version preserves all original rows and adds the computed minimum as a new field. This is equivalent to MIN() OVER(...) in SQL.

Syntax

| windowcomp min(<field>) [by <partition_field1>, <partition_field2>, ...] [sort [asc|desc] <sort_field1>, ...] [between <lower> [and <upper>] [frame_type=rows|range]] [as <alias>]

Parameters

Name Type Required Description
field numeric, string, or datetime Yes The field from which to compute the minimum value.
partition_field any No One or more fields to partition the data by (equivalent to SQL PARTITION BY).
sort_field any No One or more fields to define the order within each partition (equivalent to SQL ORDER BY). Defaults to ascending.
lower integer or null No Lower bound of the window frame. 0 = current row, negative = rows before, null = unbounded.
upper integer or null No Upper bound of the window frame. 0 = current row, positive = rows after, null = unbounded.
frame_type rows or range No Type of window frame. rows (default) uses physical row offsets; range uses value-based offsets on the sort field.
alias string No An alias for the output field.

Returns

Type: same as input field

Description: The min() function returns the minimum value within the defined window for each row. All original rows are preserved.

Usage notes

  • Row preservation: Unlike comp min(), the windowcomp min() does not reduce the number of rows. Each row retains its original data and gets an additional field with the window minimum.
  • No partitioning: If no by clause is specified, the window spans the entire result set.
  • Default frame: If no window frame is specified, the default frame is from the start of the partition to the current row (between null and 0).
  • Null handling: NULL values are ignored in the computation.
  • Running minimum: Can be combined with sort to compute running minimums.

Examples

Example 1: Minimum severity per host (all rows preserved)

Goal: Compute the minimum alert severity for each host while preserving all original rows.

XQL code:

dataset = xdr_data
| windowcomp min(alert_severity) by agent_hostname as min_severity_per_host

Explanation: The min() function computes the minimum alert_severity within each agent_hostname partition. All original rows are preserved, and each row receives the partition's minimum value in min_severity_per_host.

Output:

_TIME AGENT_HOSTNAME ALERT_SEVERITY MIN_SEVERITY_PER_HOST
2024-01-15 08:00:00 workstation-1 3 3
2024-01-15 09:00:00 workstation-1 7 3
2024-01-15 10:00:00 workstation-1 5 3
2024-01-15 08:30:00 workstation-2 2 2
2024-01-15 09:30:00 workstation-2 4 2

Example 2: Running minimum over time

Goal: Compute a running minimum of bytes received, ordered by time.

XQL code:

dataset = xdr_data
| windowcomp min(action_network_bytes_received) sort asc _time as running_min_bytes

Explanation: By sorting on _time in ascending order, the min() function computes a running minimum of action_network_bytes_received from the start of the dataset up to the current row.

Output:

_TIME ACTION_NETWORK_BYTES_RECEIVED RUNNING_MIN_BYTES
2024-01-15 08:00:00 500 500
2024-01-15 09:00:00 250 250
2024-01-15 10:00:00 400 250
2024-01-15 11:00:00 100 100

Example 3: Minimum within a sliding window of 3 rows

Goal: Compute the minimum bytes received within a sliding window of 3 rows (current row plus one row before and one row after).

XQL code:

dataset = xdr_data
| windowcomp min(action_network_bytes_received) sort asc _time between -1 and 1 as local_min

Explanation: The between -1 and 1 clause defines a sliding window of 3 rows centered on the current row. The min() function returns the minimum action_network_bytes_received value within that window for each row.

Output:

_TIME ACTION_NETWORK_BYTES_RECEIVED LOCAL_MIN
2024-01-15 08:00:00 500 250
2024-01-15 09:00:00 250 250
2024-01-15 10:00:00 400 100
2024-01-15 11:00:00 100 100

mod

Use the mod() function to calculate the remainder (modulus) of the division of two numeric values.

Syntax

mod(<dividend>, <divisor>)

Parameters

Name Type Required Description
dividend integer, float Yes The number to be divided (numerator).
divisor integer, float Yes The number to divide by (denominator). Must not be zero.

Returns

Type: integer or float (matches input types)

Description: The mod() function returns the remainder after dividing the dividend by the divisor. If either input is null or the divisor is zero, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Division by Zero: If the divisor is zero, the function returns null or raises an error.
  • Null Handling: If either input expression is null, the function returns null.
  • Sign of Result: The sign of the result matches the sign of the dividend.
  • Common Use Cases: This function is typically used within the alter stage for cyclic calculations, even/odd detection, bucketing, time-based grouping (for example, grouping by hour of day), and hash-based distribution.

Examples

Example 1: Calculate modulus of literal values

Goal: Calculate the remainder for specific division operations.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = mod(10, 3), result2 = mod(15, 5), result3 = mod(-7, 3)
| fields result1, result2, result3

Explanation: mod(10, 3) returns 1 (10 = 3�3 + 1), mod(15, 5) returns 0 (15 is evenly divisible by 5), and mod(-7, 3) returns -1 (the sign follows the dividend).

Output:

RESULT1 RESULT2 RESULT3
1 0 -1

Example 2: Determine even or odd event IDs

Goal: Classify events as having even or odd event IDs using the modulus operator.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter is_even = if(mod(event_id, 2) = 0, "even", "odd")
| fields event_id, is_even
| limit 4

Explanation: This query uses mod(event_id, 2) to check if the event ID is divisible by 2. If the remainder is 0, the event is classified as "even"; otherwise, it is "odd".

Output:

EVENT_ID IS_EVEN
101 odd
102 even
103 odd
104 even

Example 3: Group events into cyclic buckets

Goal: Distribute events into 5 cyclic buckets based on their event ID.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter bucket = mod(event_id, 5)
| fields event_id, bucket
| limit 5

Explanation: This query assigns each event to one of 5 buckets (0 through 4) using mod(event_id, 5). This creates a cyclic distribution pattern useful for load balancing or sampling.

Output:

EVENT_ID BUCKET
101 1
102 2
103 3
104 4
105 0

multiply

Use the multiply() function to calculate the product of two numbers.

Syntax

multiply (<string> | <integer> | <float>, <string> | <integer> | <float>)

Parameters

Name Type Required Description
value_1 integer, float, string Yes The first numeric value.
value_2 integer, float, string Yes The second numeric value.

Returns

The multiply() function returns the numerical product of the two input numbers.

Usage notes

  • The function accepts various numeric inputs, including numeric literals (for example, 2, 1.5), floating-point numbers, and integers.
  • The function also accepts integers or numbers provided as a string type (for example, extracted from a data field and then converted).
  • The function supports negative numbers for both input parameters.

Examples

Example 1: Multiply integer field by integer literal

Goal: Demonstrate multiply() applied directly to an integer field (event_id) and a fixed integer literal.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter multiplied_event_id = multiply(event_id, 2) 
| fields event_id, multiplied_event_id 
| limit 3 

Explanation: This query creates a new field, multiplied_event_id, by taking each event_id (for example, 101) and multiplying it by 2.

Output:

EVENT_ID MULTIPLIED_EVENT_ID
101 202
102 204
103 206

Example 2: Multiply floating-point field by integer literal

Goal: Showcase multiply() with a floating-point input field (duration_seconds) and an integer literal.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter multiplied_duration = multiply(duration_seconds, 10) 
| fields event_id, duration_seconds, multiplied_duration 
| limit 3 

Explanation: Here, duration_seconds (which contains decimal values, for example, 1.5, 0.8) is multiplied by 10, producing a new floating-point value in multiplied_duration.

Output:

EVENT_ID DURATION_SECONDS MULTIPLIED_DURATION
101 1.5 15.0
102 0.8 8.0
103 10.2 102.0

Example 3: Multiply floating-point field by floating-point literal

Goal: Demonstrate multiply() handling two floating-point numbers, including a decimal literal.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter precise_duration_calc = multiply(duration_seconds, 0.75) 
| fields event_id, duration_seconds, precise_duration_calc 
| limit 3 

Explanation: This query multiplies duration_seconds by 0.75, resulting in precise_duration_calc, which will also be a floating-point number.

Output:

EVENT_ID DURATION_SECONDS PRECISE_DURATION_CALC
101 1.5 1.125
102 0.8 0.6
103 10.2 7.65

Example 4: Multiply with a negative integer literal

Goal: Demonstrate multiply() handling a negative input, producing a negative result.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter negative_event_id = multiply(event_id, -1) 
| fields event_id, negative_event_id 
| limit 3 

Explanation: The event_id is multiplied by -1. This correctly computes the negative product, demonstrating the function's behavior with negative inputs.

Output:

EVENT_ID NEGATIVE_EVENT_ID
101 -101
102 -102
103 -103

Example 5: Multiply string-converted-to-number field by integer literal

Goal: Show multiply() applied to a numeric value extracted from a JSON field and converted from a string, demonstrating flexibility with input types.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id in (101, 110) 
| alter json_numeric_string = coalesce(simple_json_data -> code, simple_json_data -> size_gb) 
| alter json_numeric_value = to_number(json_numeric_string) 
| alter multiplied_json_value = multiply(json_numeric_value, 3) 
| fields event_id, simple_json_data, json_numeric_value, multiplied_json_value 
| limit 3 

Explanation: This query first extracts a numeric value (either code or size_gb) from the simple_json_data field as a string. The query then converts this string to a number using to_number() and applies multiply() with a literal, storing the result in multiplied_json_value.

Output:

EVENT_ID SIMPLE_JSON_DATA JSON_NUMERIC_VALUE MULTIPLIED_JSON_VALUE
101 {"status": "ok", "code": 200} 200 600
110 {"backup_id": "DB-005", "size_gb": 500} 500 1500

object_create

Use the object_create() function to construct a new object by defining specific key-value pairs.

Syntax

object_create ("<key1>", <value1>, "<key2>", <value2>, ...)

Parameters

Name Type Required Description
key_n string Yes The name of the key. This must be a string literal enclosed in double quotes.
value_n string, integer, float, boolean, object, array Yes The value associated with the preceding key. This can be a literal, a field name, or the result of another function.

Returns

The object_create() function returns a single object containing the specified key-value pairs.

Usage notes

  • The function requires an even number of arguments, structured as pairs of keys and values.
  • All keys must be provided as string literals (enclosed in double quotes).
  • Values can be of any XQL-supported data type, including strings, integers, floats, booleans, or results from other functions.
  • The function does not implicitly convert string representations of numeric or boolean values; it retains the data types exactly as entered for the values.
  • If a value parameter is NULL, the resulting object will typically contain a NULL value for that specific key.

Examples

Example 1: Creating an object with string literal key-value pairs

Goal: Construct an object using only string literals for both keys and values.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_metadata = object_create("source", "system_log", "level", "informational") 
| fields event_id, event_metadata 
| limit 3 

Explanation: This query adds a new field, event_metadata, containing a static object with two string key-value pairs for each record.

Output:

EVENT_ID EVENT_METADATA
101 {"source": "system_log", "level": "informational"}
102 {"source": "system_log", "level": "informational"}
103 {"source": "system_log", "level": "informational"}

Example 2: Creating an object with mixed data type values (literals)

Goal: Construct an object that stores values of mixed data types (string, integer, boolean, float) using literal inputs.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_details = object_create( 
    "log_id_prefix", "XDR", 
    "priority_score", 100, 
    "is_critical", true, 
    "data_size_gb", 1.5 
  ) 
| fields event_id, event_details 
| limit 3 

Explanation: A new field event_details is created, holding an object where values are of different types (string, integer, boolean, float), demonstrating the function's flexibility in value data types.

Output:

EVENT_ID EVENT_DETAILS
101 {"log_id_prefix": "XDR", "priority_score": 100, "is_critical": true, "data_size_gb": 1.5}
102 {"log_id_prefix": "XDR", "priority_score": 100, "is_critical": true, "data_size_gb": 1.5}
103 {"log_id_prefix": "XDR", "priority_score": 100, "is_critical": true, "data_size_gb": 1.5}

Example 3: Creating an object with existing field values

Goal: Construct an object using values pulled directly from existing fields in the dataset.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter contextual_info = object_create( 
    "event_type", event_description, 
    "success_status", is_successful, 
    "event_duration", duration_seconds, 
    "ip_address", ipv4_address 
  ) 
| fields event_id, event_description, is_successful, duration_seconds, ipv4_address, contextual_info 
| limit 3 

Explanation: The contextual_info field is populated with an object whose values are dynamically pulled from event_description (string), is_successful (boolean), duration_seconds (float), and ipv4_address (string) for each record.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL DURATION_SECONDS IPV4_ADDRESS CONTEXTUAL_INFO
101 "User login successful" true 1.5 192.168.1.10 {"event_type": "User login successful", "success_status": true, "event_duration": 1.5, "ip_address": "192.168.1.10"}
102 "File access attempt" false 0.8 10.0.0.1 {"event_type": "File access attempt", "success_status": false, "event_duration": 0.8, "ip_address": "10.0.0.1"}
103 "Network connection established" true 10.2 1.1.1.1 {"event_type": "Network connection established", "success_status": true, "event_duration": 10.2, "ip_address": "1.1.1.1"}

Example 4: Creating an object with values from other functions

Goal: Construct an object using the output of other XQL functions (such as len() and to_string()) as values.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter derived_object = object_create( 
    "description_length", len(event_description), 
    "id_string", to_string(event_id) 
  ) 
| fields event_id, event_description, derived_object 
| limit 3 

Explanation: The derived_object contains the length of the event_description (an integer) and the string representation of event_id, obtained using len() and to_string() functions respectively.

Output:

EVENT_ID EVENT_DESCRIPTION DERIVED_OBJECT
101 "User login successful" {"description_length": 23, "id_string": "101"}
102 "File access attempt" {"description_length": 20, "id_string": "102"}
103 "Network connection established" {"description_length": 30, "id_string": "103"}

Example 5: Handling NULL values in object_create()

Goal: Demonstrate how the function handles NULL values provided for keys or values.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter null_handling_example = object_create( 
    "domain", dst_domain, 
    "static_null_value", NULL, // Explicit NULL value 
    "constant_key", "always_present" 
  ) 
| fields event_id, dst_domain, null_handling_example 
| limit 5 

Explanation: When dst_domain is NULL (as for event_id 105), the "domain" key in the null_handling_example object also holds NULL. The "static_null_value" key consistently holds NULL due to its explicit NULL input. This demonstrates that object_create() will include NULL values for corresponding keys if the input value is NULL.

Output:

EVENT_ID DST_DOMAIN NULL_HANDLING_EXAMPLE
101 "ec2.amazonaws.com" {"domain": "ec2.amazonaws.com", "static_null_value": NULL, "constant_key": "always_present"}
102 "sts.amazonaws.com" {"domain": "sts.amazonaws.com", "static_null_value": NULL, "constant_key": "always_present"}
103 "www.google.com" {"domain": "www.google.com", "static_null_value": NULL, "constant_key": "always_present"}
104 "dropbox.com" {"domain": "dropbox.com", "static_null_value": NULL, "constant_key": "always_present"}
105 NULL {"domain": NULL, "static_null_value": NULL, "constant_key": "always_present"}

object_merge

Use the object_merge() function to construct a new object by merging the key-value pairs from two or more existing objects.

Syntax

object_merge (<obj1>, <obj2>, <obj3>, ...)

Parameters

Name Type Required Description
obj_n object Yes Two or more object parameters to be merged.

Returns

The object_merge() function returns a single, new object containing the combined key-value pairs from the input objects.

Usage notes

  • When a key name is duplicated across multiple input objects, the value associated with that key in the new, merged object is determined by the value from the latter (rightmost) argument in the function call.
  • If an entire object parameter provided to the function is NULL, the function effectively ignores that NULL input object and proceeds to merge the non-NULL objects.
  • If a key within an input object has a NULL value, that NULL value is carried over into the merged object unless it is overridden by a subsequent object in the merge sequence that defines the same key.

Examples

Example 1: Merging two simple literal objects

Goal: Combine two objects created directly from literal key-value pairs to demonstrate basic merging and key override behavior.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter 
    obj1 = object_create("name", "jane", "last_name", "doe", "age", 33), 
    obj2 = object_create("name", "jane", "last_name", "simon", "age", 34, "city", "new-york") 
| alter result = object_merge(obj1, obj2) 
| fields event_id, result 
| limit 3

Explanation: The result object contains all unique keys from obj1 and obj2. For last_name and age, the values from obj2 ("simon", 34) overwrite those from obj1 ("doe", 33) because obj2 is the latter argument in the function.

Output:

EVENT_ID RESULT
101 {"name": "jane", "last_name": "simon", "age": 34, "city": "new-york"}
102 {"name": "jane", "last_name": "simon", "age": 34, "city": "new-york"}
103 {"name": "jane", "last_name": "simon", "age": 34, "city": "new-york"}

Example 2: Merging objects with overlapping keys and new keys

Goal: Demonstrate the "latter argument wins" rule for overlapping keys and the addition of new keys when merging three objects.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter 
    initial_security_status = object_create("alert_id", "A123", "status", "new", "severity", "medium"), 
    update_details = object_create("status", "in_progress", "analyst", "JohnDoe"), 
    final_disposition = object_create("status", "closed", "resolution", "false_positive") 
| alter combined_security_info = object_merge(initial_security_status, update_details, final_disposition) 
| fields event_id, combined_security_info 
| limit 3

Explanation: The combined_security_info object incorporates keys from all three input objects. The status key's value is "closed" from final_disposition because it is the latest object in the sequence to define that key. New keys like analyst and resolution are added without conflict.

Output:

EVENT_ID COMBINED_SECURITY_INFO
101 {"alert_id": "A123", "status": "closed", "severity": "medium", "analyst": "JohnDoe", "resolution": "false_positive"}
102 {"alert_id": "A123", "status": "closed", "severity": "medium", "analyst": "JohnDoe", "resolution": "false_positive"}
103 {"alert_id": "A123", "status": "closed", "severity": "medium", "analyst": "JohnDoe", "resolution": "false_positive"}

Example 3: Merging objects with values from existing dataset fields

Goal: Construct and merge objects using values pulled directly from existing fields in the dataset.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_summary_obj = object_create( 
    "description", event_description, 
    "successful", is_successful 
  ) 
| alter network_details_obj = object_create( 
    "ip_address", ipv4_address, 
    "domain", dst_domain 
  ) 
| alter full_event_context = object_merge(event_summary_obj, network_details_obj) 
| fields event_id, full_event_context 
| limit 3

Explanation: For each record, event_summary_obj captures the event's description and success status, while network_details_obj captures network-related fields. The function then combines these dynamically created objects into full_event_context.

Output:

EVENT_ID FULL_EVENT_CONTEXT
101 {"description": "User login successful", "successful": true, "ip_address": "192.168.1.10", "domain": "ec2.amazonaws.com"}
102 {"description": "File access attempt", "successful": false, "ip_address": "10.0.0.1", "domain": "sts.amazonaws.com"}
103 {"description": "Network connection established", "successful": true, "ip_address": "1.1.1.1", "domain": "www.google.com"}

Example 4: Merging objects with values derived from other functions

Goal: Use object_merge() where some of the object's key-value pairs are results of other XQL functions (for example, to_string(), len()).

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter 
    event_ids_obj = object_create("event_id_str", to_string(event_id), "event_id_len", len(to_string(event_id))), 
    event_desc_obj = object_create("description_len", len(event_description)) 
| alter derived_and_merged_obj = object_merge(event_ids_obj, event_desc_obj) 
| fields event_id, derived_and_merged_obj 
| limit 3

Explanation: The event_ids_obj contains string and length properties derived from event_id. event_desc_obj contains the length of event_description. The function combines these, showcasing the ability to merge objects containing function-derived values.

Output:

EVENT_ID DERIVED_AND_MERGED_OBJ
101 {"event_id_str": "101", "event_id_len": 3, "description_len": 23}
102 {"event_id_str": "102", "event_id_len": 3, "description_len": 20}
103 {"event_id_str": "103", "event_id_len": 3, "description_len": 30}

Example 5: Handling NULL object inputs during merge

Goal: Illustrate behavior when one of the input objects is NULL, demonstrating that NULL input objects are effectively ignored during the merge operation.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter 
    base_info = object_create("source_product", "Cortex XDR"), 
    // Conditional creation of 'dynamic_tag': 
    // If event_id / 2 equals its floor (i.e., event_id is even), create the object; otherwise, it is NULL. 
    conditional_info = if(divide(event_id, 2) = floor(divide(event_id, 2)), object_create("dynamic_tag", "processed"), NULL), 
    static_additional_info = object_create("processing_status", "completed") 
| alter merged_data_with_null = object_merge(base_info, conditional_info, static_additional_info) 
| fields event_id, merged_data_with_null 
| limit 5

Explanation: The conditional_info object is created only for records where event_id is even. For odd event_ids, conditional_info is NULL. In these instances, the function merges base_info and static_additional_info, skipping the NULL object. For even IDs, conditional_info is a valid object and its keys are included.

Output:

EVENT_ID MERGED_DATA_WITH_NULL
101 {"source_product": "Cortex XDR", "processing_status": "completed"}
102 {"source_product": "Cortex XDR", "dynamic_tag": "processed", "processing_status": "completed"}
103 {"source_product": "Cortex XDR", "processing_status": "completed"}
104 {"source_product": "Cortex XDR", "dynamic_tag": "processed", "processing_status": "completed"}
105 {"source_product": "Cortex XDR", "processing_status": "completed"}

parse_epoch

Use the parse_epoch() function to return a Unix epoch integer value by converting a string representation of a timestamp.

Syntax

parse_epoch ("<format_string>", <timestamp field>)
parse_epoch ("<format_string>", <timestamp field>, "<time_zone>")
parse_epoch ("<format_string>", <timestamp field>, "<time_zone>", "<time_unit>")

Parameters

Name Type Required Description
format_string string Yes Defines the layout of the input timestamp string (for example, %Y-%m-%d %H:%M:%S).
timestamp_field string Yes The string containing the timestamp to be parsed.
time_zone string No An optional time zone specified by hours offset (for example, +08:00) or name (for example, America/Chicago). The default is UTC.
time_unit string No Specifies the granularity of the returned integer (SECONDS, MILLIS, MICROS). The default is SECONDS.

Returns

The parse_epoch() function returns a Unix epoch integer value.

Usage notes

  • The order of time_zone and time unit is important.
  • If you're using the time_zone argument, you must define it immediately before the time_unit argument.
  • If you use time_zone after time_unit, the default time zone (UTC) is used, and the configured value is ignored.
  • To display the returned integer as a human-readable timestamp, it is typically passed to the to_timestamp() function.

Examples

Example 1: Without a time zone or time unit configured (implicit UTC, seconds)

Goal: Convert a timestamp string into an epoch integer using the default UTC time zone and SECONDS unit, then display it as a timestamp.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_time = to_timestamp(parse_epoch("%Y-%m-%d %H:%M:%S", "2023-10-26 10:00:00")) 
| fields event_id, new_time 
| limit 3 

Explanation: The input string "2023-10-26 10:00:00" is parsed using the format "%Y-%m-%d %H:%M:%S". Because no time zone is specified, parse_epoch() interprets it as a UTC timestamp. to_timestamp() then converts the resulting epoch seconds back to a TIMESTAMP object for display.

Output:

EVENT_ID NEW_TIME
101 Oct 26th 2023 10:00:00
102 Oct 26th 2023 10:00:00
103 Oct 26th 2023 10:00:00

Example 2: With a time zone configured using an hours offset

Goal: Convert a timestamp string interpreting it relative to a +03:00 hours offset.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_time = to_timestamp(parse_epoch("%Y-%m-%d %H:%M:%S", "2023-10-26 10:00:00", "+03:00")) 
| fields event_id, new_time 
| limit 3 

Explanation: The input string 10:00:00 is interpreted as being in a +03:00 time zone. When converted to UTC (which epoch time is based on), it becomes 07:00:00 UTC. This UTC time is then returned as the TIMESTAMP object.

Output:

EVENT_ID NEW_TIME
101 Oct 26th 2023 07:00:00
102 Oct 26th 2023 07:00:00
103 Oct 26th 2023 07:00:00

Example 3: With a time zone name configured

Goal: Convert a timestamp string interpreting it relative to the "America/Chicago" time zone.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_time = to_timestamp(parse_epoch("%Y-%m-%d %H:%M:%S", "2023-10-26 10:00:00", "America/Chicago")) 
| fields event_id, new_time 
| limit 3 

Explanation: The input string 10:00:00 is interpreted as being in the "America/Chicago" time zone (UTC-5 for this date). To convert to UTC, 5 hours are added, resulting in 15:00:00 UTC.

Output:

EVENT_ID NEW_TIME
101 Oct 26th 2023 15:00:00
102 Oct 26th 2023 15:00:00
103 Oct 26th 2023 15:00:00

Example 4: With both time zone and time unit configured

Goal: Convert a timestamp string using a +03:00 hours offset and explicitly return the epoch in MILLIS (milliseconds).

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_time = to_timestamp(parse_epoch("%Y-%m-%d %H:%M:%S", "2023-10-26 10:00:00", "+03:00", "MILLIS"), "MILLIS") 
| fields event_id, new_time 
| limit 3 

Explanation: The input string is interpreted within the +03:00 time zone (converting 10:00:00 to 07:00:00 UTC). parse_epoch() generates the epoch value in milliseconds. This integer is then converted to a TIMESTAMP object using to_timestamp() with the "MILLIS" unit.

Output:

EVENT_ID NEW_TIME
101 Oct 26th 2023 07:00:00
102 Oct 26th 2023 07:00:00
103 Oct 26th 2023 07:00:00

parse_timestamp

Use the parse_timestamp() function to convert a string representation of a timestamp into a TIMESTAMP object.

Syntax

parse_timestamp ("<format_time_string>", "<time_string>" | format_string(<time field>) | <time string field> [, "<time zone>"])

Parameters

Name Type Required Description
<format_time_string> string Yes A format string that defines the layout of the input timestamp string (for example, %Y-%m-%d %H:%M:%S).
<time_string> <time_string_field> string Yes
<time_zone> string No An optional time zone specified as an hours offset (for example, +08:00) or a time zone name (for example, America/Chicago). If omitted, the default is UTC.

Returns

The parse_timestamp() function returns a TIMESTAMP object.

Usage notes

  • The <format_time_string> parameter must define the format elements that match how the input string is formatted. Every element in the input string must have a corresponding element in the format string, and their positions must match.
  • If no time zone is configured via the optional parameter, the function defaults to using the UTC time zone.
  • This function is vital for transforming human-readable date-time strings into a structured timestamp format that can be used for time-based analysis, filtering, or display.
  • parse_timestamp() can be used within an alter stage to create new timestamp fields, or within a filter stage for time-based comparisons.

Examples

Example 1: Without a time zone configured (implicit UTC)

Goal: Convert a timestamp string into a TIMESTAMP object, relying on the default UTC time zone for interpretation.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_time_field = parse_timestamp("%Y-%m-%d %H:%M:%S", "2023-10-26 10:00:00") 
| fields event_id, new_time_field 
| limit 3

Explanation: The input string "2023-10-26 10:00:00" is parsed using the format "%Y-%m-%d %H:%M:%S". Because no time zone is specified, parse_timestamp() interprets it as a UTC timestamp, which is then displayed in the specified output format.

Output:

event_id new_time_field
101 Oct 26th 2023 10:00:00
102 Oct 26th 2023 10:00:00
103 Oct 26th 2023 10:00:00

Example 2: With a time zone configured using an hours offset

Goal: Convert a timestamp string, interpreting it relative to a +03:00 hours offset.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_time_field = parse_timestamp("%Y-%m-%d %H:%M:%S", "2023-10-26 10:00:00", "+03:00") 
| fields event_id, new_time_field 
| limit 3

Explanation: The input string 10:00:00 is interpreted as being in a +03:00 time zone. When converted to UTC (which all timestamps are internally based on), it effectively becomes 10:00:00 - 03:00 = 07:00:00 UTC. This UTC time is then returned as the TIMESTAMP object, displayed in the specified format.

Output:

event_id new_time_field
101 Oct 26th 2023 07:00:00
102 Oct 26th 2023 07:00:00
103 Oct 26th 2023 07:00:00

Example 3: With a time zone name configured

Goal: Convert a timestamp string, interpreting it relative to the "America/Chicago" time zone.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter new_time_field = parse_timestamp("%Y-%m-%d %H:%M:%S", "2023-10-26 10:00:00", "America/Chicago") 
| fields event_id, new_time_field 
| limit 3

Explanation: The input string 10:00:00 is interpreted as being in the "America/Chicago" time zone. To convert this local time to UTC, you add the offset. If Chicago is UTC-5 (CDT), then 10:00:00 + 05:00 = 15:00:00 UTC. This UTC time is then returned as the TIMESTAMP object, displayed in the specified format.

Output:

event_id new_time_field
101 Oct 26th 2023 15:00:00
102 Oct 26th 2023 15:00:00
103 Oct 26th 2023 15:00:00

Example 4: Converting a time string that contains milliseconds

Goal: Convert a time string that includes fractional seconds (milliseconds) using specific format elements.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
// Use %E3S to capture 3 digits of fractional seconds, commonly used for milliseconds 
| alter time_string_with_millis = "2023-10-26 10:00:00.723" // Example string with milliseconds
| alter new_time_field = parse_timestamp("%Y-%m-%d %H:%M:%E3S", time_string_with_millis) //"%H:%M:%E3S" is also equivalent to "%R:%E3S", both matching "10:00:00.723" 
| fields event_id, time_string_with_millis, new_time_field 
| limit 3

Explanation: The parse_timestamp() function successfully processes the input string time_string_with_millis using the format string that includes %E3S to account for seconds and three digits of fractional seconds. Although the internal TIMESTAMP object will store the milliseconds, the final displayed new_time_field conforms to the default format, truncating the fractional part in the output.

Output:

event_id time_string_with_millis new_time_field
101 2023-10-26 10:00:00.723 Oct 26th 2023 10:00:00
102 2023-10-26 10:00:00.723 Oct 26th 2023 10:00:00
103 2023-10-26 10:00:00.723 Oct 26th 2023 10:00:00

pow

Use the pow() function to calculate the value of a number raised to the power of another number.

Syntax

pow (<base>, <exponent>)

Parameters

Name Type Required Description
base integer, float, string Yes The base number to be raised.
exponent integer, float, string Yes The exponent to which the base is raised.

Returns

The pow() function returns the numerical result of the base raised to the power of the exponent.

Usage notes

  • The function accepts numeric literals (for example, 2, 1.5), floating-point numbers, and integers as input.
  • Input values can also be provided as strings representing numbers (for example, extracted from a data field), which the function will convert for calculation.
  • The function supports negative numbers for both the base and the exponent.

Examples

Example 1: Literal base and literal integer exponent (calculating 1 MB and 1 GB)

Goal: Calculate common byte sizes (Megabytes and Gigabytes) using integer literals, where 1MB is 2^20 bytes and 1GB is 2^30 bytes.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter mega_bytes = pow(2, 20)
| alter giga_bytes = pow(2, 30)
| fields event_id, mega_bytes, giga_bytes
| limit 3

Explanation: This query creates two new fields, mega_bytes and giga_bytes, by raising the literal 2 to the powers of 20 and 30 respectively. The result, 1048576 for 1MB and 1073741824 for 1GB, is a constant numerical value for each record.

Output:

EVENT_ID MEGA_BYTES GIGA_BYTES
101 1048576 1073741824
102 1048576 1073741824
103 1048576 1073741824

Example 2: Field base and literal integer exponent

Goal: Use a floating-point field as the base for a power calculation to square the value.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter duration_squared = pow(duration_seconds, 2)
| fields event_id, duration_seconds, duration_squared
| limit 3

Explanation: The duration_seconds field (for example, 1.5, 0.8, 10.2) is raised to the power of 2 (squared). For instance, 1.5 becomes 2.25, and 0.8 becomes 0.64, demonstrating the function's precision with floating-point numbers.

Output:

EVENT_ID DURATION_SECONDS DURATION_SQUARED
101 1.5 2.25
102 0.8 0.64
103 10.2 104.04

Example 3: Field base from array and literal integer exponent

Goal: Use an element extracted from an array field as the base for a power calculation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter first_numeric_code = arrayindex(numeric_codes, 0)
| alter code_cubed = pow(first_numeric_code, 3)
| fields event_id, numeric_codes, first_numeric_code, code_cubed
| limit 3

Explanation: This query extracts the first element (index 0) from the numeric_codes array for each record. The query then raises this extracted integer to the power of 3 (cubed), storing the result in code_cubed.

Output:

EVENT_ID NUMERIC_CODES FIRST_NUMERIC_CODE CODE_CUBED
101 [13, -47, 29, 82, -15] 13 2197
102 [-21, 56, 13, -88, 42] -21 -9261
103 [90, -33, 7, 51, -62, 18] 90 729000

Example 4: Literal base and negative integer exponent from array

Goal: Raise a literal base to the power of a negative exponent extracted from an array field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter second_numeric_code = arrayindex(numeric_codes, 1)
| alter base_powered_by_code = pow(10, second_numeric_code)
| fields event_id, numeric_codes, second_numeric_code, base_powered_by_code
| limit 3

Explanation: For event_id 101, second_numeric_code is -47. The calculation becomes 10^-47, resulting in a very small floating-point number (scientific notation 1e-47). This illustrates how pow() correctly handles negative exponents, producing decimal results.

Output:

EVENT_ID NUMERIC_CODES SECOND_NUMERIC_CODE BASE_POWERED_BY_CODE
101 [13, -47, 29, 82, -15] -47 1e-47
102 [-21, 56, 13, -88, 42] 56 1e+56
103 [90, -33, 7, 51, -62, 18] -33 1e-33

Example 5: Negative base and literal integer exponent

Goal: Calculate the power of a negative base number.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter pow_negative_base = pow(-5, 3)
| fields event_id, pow_negative_base
| limit 3

Explanation: This calculates -5 multiplied by itself three times (-5 -5 -5), resulting in -125. The result is consistently -125 across records as the inputs are literals.

Output:

EVENT_ID POW_NEGATIVE_BASE
101 -125
102 -125
103 -125

Example 6: Field base and zero exponent

Goal: Demonstrate the mathematical rule that any non-zero number raised to the power of zero equals one.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter zero_exponent_result = pow(event_id, 0)
| fields event_id, zero_exponent_result
| limit 3

Explanation: Regardless of the event_id value (as long as it's non-zero), pow(<number>, 0) will always return 1, following standard mathematical rules.

Output:

EVENT_ID ZERO_EXPONENT_RESULT
101 1
102 1
103 1

Example 7: Extracting from JSON and applying pow

Goal: Extract a numeric value from JSON data and use it as the base for a power calculation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter simple_json_data != null
| alter json_code_string = simple_json_data -> code
| alter json_numeric_value = to_number(json_code_string)
| alter code_to_power_of_two = pow(json_numeric_value, 2)
| fields event_id, simple_json_data, json_numeric_value, code_to_power_of_two
| limit 3

Explanation: This query first extracts the code value (for example, "200") from the simple_json_data field as a string. The query then converts this string to a number using to_number() and applies pow() with a literal exponent, storing the result in code_to_power_of_two.

Output:

EVENT_ID SIMPLE_JSON_DATA JSON_NUMERIC_VALUE CODE_TO_POWER_OF_TWO
101 {"status": "ok", "code": 200} 200 40000

power

Use the power() function to calculate the value of a number raised to the power of another number. This function is an alias for pow().

Syntax

power(<base>, <exponent>)

Parameters

Name Type Required Description
base integer, float Yes The base number to be raised.
exponent integer, float Yes The exponent to which the base is raised.

Returns

Type: float

Description: The power() function returns the numerical result of the base raised to the power of the exponent. If either input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Alias: power() is functionally identical to pow(). Both functions can be used interchangeably.
  • Valid Input Range: The function accepts any numeric values for both base and exponent, including negative numbers and zero.
  • Null Handling: If either input expression is null, the function returns null.
  • Special Cases: power(x, 0) returns 1.0 for any non-zero x. power(0, 0) returns 1.0. power(0, n) returns 0.0 for n > 0.
  • Negative Base: When the base is negative and the exponent is not an integer, the result may be NaN.
  • Common Use Cases: This function is typically used within the alter stage for exponential calculations, area/volume computations, and scientific formulas.

Examples

Example 1: Calculate power of literal values

Goal: Calculate specific power operations to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = power(2, 10), result2 = power(3, 3), result3 = power(10, -2)
| fields result1, result2, result3

Explanation: power(2, 10) returns 1024 (2^10), power(3, 3) returns 27 (3^3), and power(10, -2) returns 0.01 (10^-2 = 1/100).

Output:

RESULT1 RESULT2 RESULT3
1024 27 0.01

Example 2: Square field values using power

Goal: Calculate the square of values stored in a dataset field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter squared_value = power(duration_seconds, 2)
| fields event_id, duration_seconds, squared_value
| limit 3

Explanation: This query squares each value in the duration_seconds field using power() with an exponent of 2. For example, 1.5 squared is 2.25.

Output:

EVENT_ID DURATION_SECONDS SQUARED_VALUE
101 1.5 2.25
102 0.8 0.64
103 10.2 104.04

Example 3: Calculate square root using power with fractional exponent

Goal: Demonstrate that power(x, 0.5) is equivalent to the square root.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value > 0
| alter sqrt_via_power = power(numeric_value, 0.5)
| alter sqrt_direct = sqrt(numeric_value)
| fields event_id, numeric_value, sqrt_via_power, sqrt_direct
| limit 3

Explanation: This query demonstrates that raising a number to the power of 0.5 is equivalent to taking its square root. Both power(x, 0.5) and sqrt(x) produce the same result.

Output:

EVENT_ID NUMERIC_VALUE SQRT_VIA_POWER SQRT_DIRECT
101 4.0 2.0 2.0
102 9.0 3.0 3.0
103 16.0 4.0 4.0

rand

Use the rand() function to generate a pseudo-random floating-point number between 0 (inclusive) and 1 (exclusive).

Syntax

rand()

Parameters

This function takes no parameters.

Returns

Type: float

Description: The rand() function returns a pseudo-random floating-point number in the range 0, 1). Each invocation produces a different random value.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • No Arguments: The rand() function takes no parameters and generates a new random value each time it is called.
  • Range: The returned value is always ≥ 0.0 and < 1.0.
  • Non-Deterministic: Each call to rand() produces a different value. Results are not reproducible across query executions.
  • Scaling: To generate random numbers in a different range, multiply the result by the desired range and add an offset. For example, add(multiply(rand(), 100), 1) generates a random number between 1 and 101.
  • Common Use Cases: This function is typically used within the alter stage for random sampling, generating test data, randomized ordering, and probabilistic analysis.

Examples

Example 1: Generate random values for each record

Goal: Add a random value to each record in the dataset.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter random_val = rand()
| fields event_id, random_val
| limit 3

Explanation: This query generates a random floating-point number between 0 and 1 for each record and stores it in random_val. Each record receives a different random value.

Output:

EVENT_ID RANDOM_VAL
101 0.73214
102 0.15892
103 0.94501

Example 2: Random sampling of records

Goal: Randomly sample approximately 50% of records from the dataset.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter sample_flag = rand()
| filter sample_flag < 0.5
| fields event_id, source_name
| limit 5

Explanation: This query assigns a random value to each record, then filters to keep only records where the random value is less than 0.5, effectively sampling approximately 50% of the data.

Output:

EVENT_ID SOURCE_NAME
102 server-01
104 server-03
107 server-02
109 server-01
111 server-03

Example 3: Generate random integers in a specific range

Goal: Generate random integers between 1 and 100 for each record.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter random_int = to_integer(add(multiply(rand(), 100), 1))
| fields event_id, random_int
| limit 3

Explanation: This query scales the random value from [0, 1) to [1, 101) by multiplying by 100 and adding 1, then converts to an integer using to_integer() to produce whole numbers between 1 and 100.

Output:

EVENT_ID RANDOM_INT
101 47
102 83
103 12

range_bucket

Use the range_bucket() function to determine which bucket a numeric value falls into, given an array of bucket boundaries. The function returns the index of the bucket (0-based) that contains the value.

Syntax

range_bucket(<value>, <boundaries>)

Parameters

Name Type Required Description
value integer, float Yes The numeric value to classify into a bucket.
boundaries array of integers or floats Yes A sorted array of boundary values that define the bucket edges. Must be in ascending order.

Returns

Type: integer

Description: The range_bucket() function returns a 0-based integer index indicating which bucket the value falls into. If the value is less than the first boundary, it returns 0. If the value is greater than or equal to the last boundary, it returns the number of boundaries. If the input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Sorted Boundaries: The boundaries array must be sorted in ascending order. Unsorted boundaries produce undefined results.
  • Bucket Ranges: For boundaries [b1, b2, b3], the buckets are: bucket 0 = (-∞, b1), bucket 1 = [b1, b2), bucket 2 = [b2, b3), bucket 3 = [b3, +∞).
  • Null Handling: If the value is null, the function returns null.
  • Common Use Cases: This function is typically used within the alter stage for data binning, histogram creation, severity classification, and score grading.

Examples

Example 1: Classify values into predefined buckets

Goal: Classify numeric values into severity levels using predefined boundaries.

XQL code:

dataset = xdr_data
| limit 1
| alter bucket1 = range_bucket(5, arraycreate(0, 10, 50, 100))
| alter bucket2 = range_bucket(25, arraycreate(0, 10, 50, 100))
| alter bucket3 = range_bucket(75, arraycreate(0, 10, 50, 100))
| fields bucket1, bucket2, bucket3

Explanation: With boundaries [0, 10, 50, 100], the value 5 falls in bucket 1 (between 0 and 10), 25 falls in bucket 2 (between 10 and 50), and 75 falls in bucket 3 (between 50 and 100).

Output:

BUCKET1 BUCKET2 BUCKET3
1 2 3

Example 2: Classify field values into risk levels

Goal: Assign risk levels to events based on their numeric value using bucket boundaries.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter risk_bucket = range_bucket(numeric_value, arraycreate(0, 25, 50, 75, 100))
| alter risk_level = if(risk_bucket = 0, "none", risk_bucket = 1, "low", risk_bucket = 2, "medium", risk_bucket = 3, "high", "critical")
| fields event_id, numeric_value, risk_bucket, risk_level
| limit 3

Explanation: This query classifies each numeric_value into a risk bucket using boundaries [0, 25, 50, 75, 100], then maps each bucket to a human-readable risk level label.

Output:

EVENT_ID NUMERIC_VALUE RISK_BUCKET RISK_LEVEL
101 5.0 1 low
102 60.0 3 high
103 30.0 2 medium

Example 3: Create histogram buckets for duration values

Goal: Distribute duration values into histogram buckets for analysis.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter duration_bucket = range_bucket(duration_seconds, arraycreate(1, 5, 10, 30, 60))
| comp count(event_id) as event_count by duration_bucket
| fields duration_bucket, event_count
| sort asc duration_bucket

Explanation: This query assigns each event to a duration bucket based on duration_seconds, then counts the number of events in each bucket. This creates a histogram-like distribution of event durations.

Output:

DURATION_BUCKET EVENT_COUNT
0 3
1 5
2 4
3 2
4 1

rank (windowcomp)

Use the rank() function within the windowcomp stage to assign a rank to each row within a partition based on the specified sort order. Rows with equal values in the sort field receive the same rank, and the next rank is incremented by the number of tied rows (i.e., ranks may have gaps). This is equivalent to RANK() OVER(...) in SQL.

Syntax

| windowcomp rank() [by <partition_field1>, <partition_field2>, ...] sort [asc|desc] <sort_field1> [, [asc|desc] <sort_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
partition_field any No One or more fields to partition the data by (equivalent to SQL PARTITION BY).
sort_field any Yes One or more fields to define the order within each partition (equivalent to SQL ORDER BY). Required for numbering functions. Defaults to ascending.
alias string No An alias for the output field.

Returns

Type: integer

Description: The rank() function returns an integer rank for each row within its partition. Rows with identical sort values receive the same rank, and subsequent ranks skip accordingly (for example, 1, 2, 2, 4).

Usage notes

  • Sort required: The sort clause is mandatory for the rank() function.
  • Numbering function: rank() is a numbering function and cannot be used with window frames (between clause).
  • Tied values: When multiple rows have the same value in the sort field, they receive the same rank. The next rank after a tie skips the number of tied rows.
  • Difference from row_number: Unlike row_number(), which always assigns unique sequential numbers, rank() assigns the same number to tied rows and leaves gaps.
  • Difference from dense_rank: Unlike dense_rank(), which does not leave gaps after ties, rank() leaves gaps (for example, 1, 2, 2, 4 instead of 1, 2, 2, 3).

Examples

Example 1: Rank alerts by severity within each host

Goal: Rank alerts by severity (highest first) for each host.

XQL code:

dataset = xdr_data
| windowcomp rank() by agent_hostname sort desc alert_severity as severity_rank

Explanation: The rank() function assigns a rank to each row within each agent_hostname partition, ordered by alert_severity in descending order. Alerts with the same severity receive the same rank.

Output:

_TIME AGENT_HOSTNAME ALERT_SEVERITY SEVERITY_RANK
2024-01-15 09:00:00 workstation-1 7 1
2024-01-15 10:00:00 workstation-1 5 2
2024-01-15 11:00:00 workstation-1 5 2
2024-01-15 08:00:00 workstation-1 3 4

Example 2: Rank events by time globally

Goal: Rank all events by time in ascending order without partitioning.

XQL code:

dataset = xdr_data
| windowcomp rank() sort asc _time as time_rank

Explanation: Without a by clause, the rank() function ranks all rows across the entire result set by _time in ascending order.

Output:

_TIME AGENT_HOSTNAME TIME_RANK
2024-01-15 08:00:00 workstation-1 1
2024-01-15 08:00:00 workstation-2 1
2024-01-15 09:00:00 workstation-1 3
2024-01-15 10:00:00 workstation-2 4

Example 3: Find top-ranked events per category

Goal: Rank events by bytes sent within each event category and filter for the top 3.

XQL code:

dataset = xdr_data
| windowcomp rank() by event_type sort desc bytes_sent as bytes_rank
| filter bytes_rank <= 3

Explanation: The rank() function assigns ranks within each event_type partition based on bytes_sent in descending order. The subsequent filter stage keeps only the top 3 ranked rows per category.

Output:

_TIME EVENT_TYPE BYTES_SENT BYTES_RANK
2024-01-15 09:00:00 NETWORK 5000 1
2024-01-15 10:00:00 NETWORK 4500 2
2024-01-15 11:00:00 NETWORK 3000 3
2024-01-15 08:00:00 PROCESS 2000 1
2024-01-15 12:00:00 PROCESS 1500 2
2024-01-15 13:00:00 PROCESS 1000 3

regexcapture

Use the regexcapture() function to extract from a string substrings that match specified named regular expression groups and return them as a JSON object.

Syntax

regexcapture (<field>, "<pattern>")

Parameters

Name Type Required Description
field string Yes The string field (typically _raw_log) or literal to apply the regex to.
pattern string Yes The regular expression string, enclosed in double quotes, containing named capture groups ((?P<name>...)).

Returns

The regexcapture() function returns a JSON object where the keys correspond to the named capture groups defined in the regex pattern, and the values are the extracted substrings.

Usage notes

  • The regexcapture() function is only supported in the XQL syntax for parsing rules. The function cannot be used directly in an alter or filter stage within an interactive XQL query submitted via the Query Builder.
  • XQL utilizes the RE2 regular expression implementation.
  • For case-insensitive matching, you can include (?i) at the beginning of your regular expression pattern. This syntax must be added only once at the beginning of the inline regular expression.
  • Unlike the regextract() function, which typically supports only one capturing group in queries, regexcapture() is designed for capturing multiple named groups within a single pattern.
  • The function is ideal for scenarios where the exact regex pattern might vary across logs, offering flexible extraction into structured JSON.

Examples

Example 1: Extracting user and IP address from a login log

Goal: Extract the username and source IP address from a raw log using named capture groups.

XQL code:

// This example demonstrates how regexcapture() is defined in a Parsing Rule
// and simulates the output using standard XQL functions for display purposes.

// Parsing Rule Syntax:
// alter captured_details = regexcapture(_raw_log, "User (?P<username>\w+) logged in from (?P<source_ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})");

// Simulated Query to visualize the result:
config timeframe = 1d
| dataset = sample_xql_raw
| alter log_entry = raw_log_data
| alter extracted_info =
    if(event_id = 101,
       object_create(
           "username", "Alice",
           "source_ip", "192.168.1.10"
       ),
       NULL
    )
| fields event_id, log_entry, extracted_info
| limit 1

Explanation: A conceptual Parsing Rule would apply the regex pattern to the raw log. The named groups username and source_ip would capture "Alice" and "192.168.1.10" respectively, forming the JSON object {"username": "Alice", "source_ip": "192.168.1.10"} in the extracted_info field.

Output:

EVENT_ID LOG_ENTRY EXTRACTED_INFO
101 "User Alice logged in from 192.168.1.10" {"username": "Alice", "source_ip": "192.168.1.10"}

Example 2: Extracting process name and file path from an access log

Goal: Extract a process name and a file path from a system log entry.

XQL code:

// This example demonstrates how regexcapture() is defined in a Parsing Rule
// and simulates the output using standard XQL functions for display purposes.

// Parsing Rule Syntax:
// alter captured_process_data = regexcapture(_raw_log, "Process (?P<process_name>[a-zA-Z0-9.]+?) attempted to access (?P<file_path>.*)");

// Simulated Query to visualize the result:
config timeframe = 1d
| dataset = sample_xql_raw
| alter log_entry = raw_log_data
| alter extracted_info =
    if(event_id = 102,
       object_create(
           "process_name", "cmd.exe",
           "file_path", "/etc/passwd"
       ),
       NULL
    )
| fields event_id, log_entry, extracted_info
| limit 1

Explanation: The conceptual Parsing Rule extracts "cmd.exe" into process_name and "/etc/passwd" into file_path, creating the corresponding JSON object.

Output:

EVENT_ID LOG_ENTRY EXTRACTED_INFO
102 "Process cmd.exe attempted to access /etc/passwd" {"process_name": "cmd.exe", "file_path": "/etc/passwd"}

Example 3: Extracting destination IP address, port, and application ID (with case-insensitive matching)

Goal: Extract network details and an application ID from a network connection log using case-insensitive matching.

XQL code:

// This example demonstrates how regexcapture() is defined in a Parsing Rule
// and simulates the output using standard XQL functions for display purposes.

// Parsing Rule Syntax:
// alter captured_network_details = regexcapture(_raw_log, "(?i)connection to (?P<dest_ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(?P<port>\d+) initiated by (?P<app_id>[^ ]+)");

// Simulated Query to visualize the result:
config timeframe = 1d
| dataset = sample_xql_raw
| alter log_entry = raw_log_data
| alter extracted_info =
    if(event_id = 103,
       object_create(
           "dest_ip", "1.1.1.1",
           "port", "443",
           "app_id", "AppX"
       ),
       NULL
    )
| fields event_id, log_entry, extracted_info
| limit 1

Explanation: The conceptual Parsing Rule uses (?i) for case-insensitivity and extracts the destination IP, port, and application ID, demonstrating the capability to parse varied log structures into structured JSON objects.

Output:

EVENT_ID LOG_ENTRY EXTRACTED_INFO
103 "Outbound connection to 1.1.1.1:443 initiated by AppX" {"dest_ip": "1.1.1.1", "port": "443", "app_id": "AppX"}

regextract

Use the regextract() function to extract a substring from a field value using a regular expression pattern. The function returns the first captured group from the regex match. This is a scalar function used within the alter stage.

Syntax

| alter <output_field> = regextract(<field>, "<regex_pattern>")

Parameters

Name Type Required Description
field string Yes The string field from which to extract a substring.
regex_pattern string (regex) Yes A regular expression pattern with at least one capture group (). The function returns the content matched by the first capture group.

Returns

Type: string

Description: The regextract() function returns the substring matched by the first capture group in the regular expression. Returns NULL if the pattern does not match the input string or if the input field is NULL.

Usage notes

  • Capture groups: The regex pattern must contain at least one capture group defined by parentheses (). Only the first capture group is returned.
  • No match: If the regex does not match the input string, the function returns NULL.
  • Case sensitivity: Regular expression matching is case-sensitive by default.
  • Escape characters: Special regex characters must be escaped with a backslash \ (for example, \. to match a literal dot).
  • Scalar function: regextract() is a scalar function used in the alter stage, not an aggregation function. The function operates on each row individually.
  • Multiple extractions: To extract multiple parts from a string, use multiple alter statements with different capture groups.

Examples

Example 1: Extract domain from email address

Goal: Extract the domain portion from an email address field.

XQL code:

dataset = xdr_data
| alter domain = regextract(actor_effective_username, "@(.+)$")

Explanation: The regex @(.+)$ matches the @ symbol followed by one or more characters until the end of the string. The capture group (.+) captures the domain portion.

Output:

ACTOR_EFFECTIVE_USERNAME DOMAIN
admin@example.com example.com
user1@corp.local corp.local
svc_account@internal.net internal.net

Example 2: Extract IP address from a log message

Goal: Extract an IPv4 address from a free-text log message.

XQL code:

dataset = xdr_data
| alter extracted_ip = regextract(action_file_path, "(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})")

Explanation: The regex pattern (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) matches an IPv4 address pattern. The entire match is captured by the outer parentheses.

Output:

ACTION_FILE_PATH EXTRACTED_IP
Connection from 192.168.1.100 on port 443 192.168.1.100
Failed login attempt from 10.0.0.5 10.0.0.5
No IP in this entry null

Example 3: Extract file extension from file path

Goal: Extract the file extension from a file path.

XQL code:

dataset = xdr_data
| alter file_ext = regextract(action_file_path, "\.([^.]+)$")

Explanation: The regex \.([^.]+)$ matches a literal dot followed by one or more non-dot characters at the end of the string. The capture group ([^.]+) captures the file extension without the dot.

Output:

ACTION_FILE_PATH FILE_EXT
C:\Windows\System32\cmd.exe exe
/var/log/syslog.log log
/tmp/archive.tar.gz gz

replace

Use the replace() function to substitute all occurrences of a specified substring within a string field with a new replacement string.

Syntax

replace (<field>, "<old_substring>", "<new_string>")

Parameters

Name Type Required Description
field string Yes The string field or literal value you wish to modify.
old_substring string Yes The specific substring to be found and replaced. This must be enclosed in double quotes.
new_string string Yes The string that will replace all occurrences of the old_substring. This must also be enclosed in double quotes.

Returns

The replace() function returns a new string with the replacements made. If the old_substring is not found, the original string is returned unchanged.

Usage notes

  • The replace() function operates exclusively on string inputs.
  • The function replaces all occurrences of the specified old_substring within the input field.
  • By default, replace() is case-sensitive. "user" will not replace "User" unless explicitly handled (for example, by combining with lowercase()).
  • replace() is typically used within the alter stage to create new fields or modify existing ones.

Examples

Example 1: Simple replacement of a literal substring

Goal: Replace a specific word ("User") in raw_log_data with a new word ("Client").

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter raw_log_data contains "User" 
| alter modified_log = replace(raw_log_data, "User", "Client") 
| fields event_id, raw_log_data, modified_log 

Explanation: The query finds the literal string "User" in the raw_log_data for event_id 101 and replaces it with "Client", creating the modified_log field.

Output:

EVENT_ID RAW_LOG_DATA MODIFIED_LOG
101 "User Alice logged in from 192.168.1.10" "Client Alice logged in from 192.168.1.10"

Example 2: Replacing with an empty string (removal)

Goal: Remove a specific phrase (" attempted to access") from raw_log_data by replacing it with an empty string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter raw_log_data contains "attempted to access"
| alter cleaned_log = replace(raw_log_data, " attempted to access", "") 
| fields event_id, raw_log_data, cleaned_log 

Explanation: The specified phrase is entirely removed from the raw_log_data, resulting in a more concise cleaned_log.

Output:

EVENT_ID RAW_LOG_DATA CLEANED_LOG
102 "Process cmd.exe attempted to access /etc/passwd" "Process cmd.exe /etc/passwd"

Example 3: Case-sensitive replacement

Goal: Demonstrate that replace() is case-sensitive by attempting to replace "user login" with "user logon" in event_description where "User login" is present.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_description contains "login"
| alter case_sensitive_change = replace(event_description, "user login", "user logon") 
| fields event_id, event_description, case_sensitive_change 

Explanation: Since replace() is case-sensitive, and "user login" (lowercase) does not exactly match "User login" (as it appears in "User login successful" which has an uppercase 'U'), no replacement occurs.

Output:

EVENT_ID EVENT_DESCRIPTION CASE_SENSITIVE_CHANGE
101 "User login successful" "User login successful"

Example 4: Combining with lowercase() for case-insensitive replacement logic

Goal: Use lowercase() to standardize the input string before applying replace(), achieving a conceptual case-insensitive replacement.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_description contains "connection"
| alter lower_event_desc = lowercase(event_description) 
| alter case_insensitive_replace = replace(lower_event_desc, "network", "link") 
| fields event_id, event_description, lower_event_desc, case_insensitive_replace 

Explanation: By first converting event_description to lowercase, replace() successfully finds and replaces "network" with "link", demonstrating how to implement case-insensitive logic.

Output:

EVENT_ID EVENT_DESCRIPTION LOWER_EVENT_DESC CASE_INSENSITIVE_REPLACE
103 "Network connection established" "network connection established" "link connection established"

Example 5: Replacing a character with another character

Goal: Replace all spaces in event_description with underscores for a specific event.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 104 
| alter underscored_description = replace(event_description, " ", "_") 
| fields event_id, event_description, underscored_description 

Explanation: All spaces within the event_description are replaced by underscores.

Output:

EVENT_ID EVENT_DESCRIPTION UNDERSCORED_DESCRIPTION
104 "System heartbeat" "System_heartbeat"

Example 6: Handling non-existent substrings

Goal: Demonstrate that if the old_substring is not found, the original string is returned unmodified.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 101 
| alter no_change_log = replace(raw_log_data, "NonExistentPhrase", "NewPhrase") 
| fields event_id, raw_log_data, no_change_log 

Explanation: Since "NonExistentPhrase" is not present in raw_log_data, the no_change_log field retains the original value of raw_log_data.

Output:

EVENT_ID RAW_LOG_DATA NO_CHANGE_LOG
101 "User Alice logged in from 192.168.1.10" "User Alice logged in from 192.168.1.10"

replex

Use the replex() function to identify a substring (pattern) and replace it with a new string.

Syntax

replex (<string>, <pattern>, <new_string>)

Parameters

Name Type Required Description
string string Yes The string field or literal value you want to modify.
pattern string Yes The regular expression pattern to find and replace. The pattern must be enclosed in double quotes.
new_string string Yes The string that will replace all occurrences of the matched pattern. This string must also be enclosed in double quotes.

Returns

The replex() function returns a new string with the replacements made. If the pattern is not found, the original string is returned unchanged.

Usage notes

  • The replex() function operates exclusively on string inputs.
  • The function replaces all occurrences of the specified regular expression pattern within the input field.
  • XQL uses the RE2 regular expression implementation.
  • By default, regex patterns are case-sensitive. To achieve case-insensitive matching, the (?i) syntax should be added once at the beginning of the inline regular expression within the pattern string.
  • The replex() function is typically used within the alter stage to create new fields or modify existing ones.

Examples

Example 1: Simple pattern replacement (masking a specific IP address)

Goal: Replace a specific IP address (192.168.1.10) in the raw_log_data field with a placeholder INTERNAL_IP.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 101 
| alter masked_log = replex(raw_log_data, "192\.168\.1\.10", "INTERNAL_IP") 
| fields event_id, raw_log_data, masked_log 

Explanation: The query finds the literal IP "192.168.1.10" (escaped dots for regex literal match) in raw_log_data for event_id 101 and replaces it with "INTERNAL_IP", creating the masked_log field.

Output:

EVENT_ID RAW_LOG_DATA MASKED_LOG
101 "User Alice logged in from 192.168.1.10" "User Alice logged in from INTERNAL_IP"

Example 2: Using regex character classes for general IP address masking

Goal: Use a regular expression to find and mask any standard IPv4 address pattern in the raw_log_data or ipv4_address fields.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id in (101, 103)
| alter masked_ipv4 = replex(ipv4_address, "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", "MASKED_ADDR") 
| alter masked_raw_log = replex(raw_log_data, "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", "MASKED_IP") 
| fields event_id, ipv4_address, masked_ipv4, raw_log_data, masked_raw_log 

Explanation: The query uses the regex \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} to identify and replace dotted-decimal IPv4 addresses. For event 101, ipv4_address is masked. For event 103, the IP in raw_log_data is masked.

Output:

EVENT_ID IPV4_ADDRESS MASKED_IPV4 RAW_LOG_DATA MASKED_RAW_LOG
101 "192.168.1.10" "MASKED_ADDR" "User Alice logged in from 192.168.1.10" "User Alice logged in from MASKED_IP"
103 NULL NULL "Outbound connection to 1.1.1.1:443 initiated by AppX" "Outbound connection to MASKED_IP:443 initiated by AppX"

Example 3: Replacing multiple occurrences of a word

Goal: Target raw_log_data and replace multiple instances of whitespace characters with "_".

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 103
| alter updated_log = replex(raw_log_data, "\s", "_") 
| fields event_id, raw_log_data, updated_log 

Explanation: The replex() function finds all instances of the whitespace character " " and replaces them with "_".

Output:

EVENT_ID RAW_LOG_DATA UPDATED_LOG
103 "Outbound connection to 1.1.1.1:443 initiated by AppX" "Outbound_connection_to_1.1.1.1:443_initiated_by_AppX"

Example 4: Case-insensitive replacement using (?i)

Goal: Perform a case-insensitive replacement by using the (?i) flag to match "User" or "user" regardless of capitalization, replacing it with "ACCOUNT_HOLDER" in event_description.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 101 
| alter anonymized_desc = replex(event_description, "(?i)user", "ACCOUNT_HOLDER") 
| fields event_id, event_description, anonymized_desc 

Explanation: By prefixing the regex with (?i), replex() ignores case, successfully replacing "User" (with a capital 'U') in event_description with "ACCOUNT_HOLDER".

Output:

EVENT_ID EVENT_DESCRIPTION ANONYMIZED_DESC
101 "User login successful" "ACCOUNT_HOLDER login successful"

Example 5: Removing a pattern (replacing with an empty string)

Goal: Remove the " from " followed by an IP address from raw_log_data by replacing the pattern with an empty string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter cleaned_log = replex(raw_log_data, " from \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", "") 
| fields event_id, raw_log_data, cleaned_log 
| filter event_id = 101 

Explanation: The regex matches " from " followed by any IPv4 pattern. replex() then replaces this entire matched substring with an empty string, effectively removing it and cleaning the raw_log_data entry.

Output:

EVENT_ID RAW_LOG_DATA CLEANED_LOG
101 "User Alice logged in from 192.168.1.10" "User Alice logged in"

round

Use the round() function to take a numeric input (either a float or an integer) and return the value rounded to the nearest whole integer.

Syntax

round (<float> | <integer>)

Parameters

Name Type Required Description
value float, integer Yes The numeric value to be rounded.

Returns

The round() function returns an integer representing the input value rounded to the nearest whole number. Standard mathematical rounding rules apply, where values exactly at .5 typically round up.

Usage notes

  • The function accepts either a floating-point number or an integer as its input.
  • If the input is already an integer, the value remains unchanged.
  • For positive numbers, a fractional part of .5 or greater rounds up to the next higher integer.
  • For negative numbers, the function rounds to the nearest integer, adhering to standard mathematical rounding for negative values.

Examples

Example 1: Round on a positive floating-point field

Goal: Round a positive floating-point field to the nearest integer, demonstrating rounding up and rounding down.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter rounded_duration_up = round(duration_seconds)
| alter example_duration = 1.4
| alter rounded_duration_down = round(example_duration)
| fields event_id, duration_seconds, rounded_duration_up, example_duration, rounded_duration_down
| limit 3

Explanation: This query calculates rounded_duration_up by rounding duration_seconds. For event_id 101 (duration_seconds of 1.5), it rounds to 2. For event_id 102 (duration_seconds of 0.8), it rounds to 1. A literal 1.4 is introduced to show it rounds down to 1.

Output:

EVENT_ID DURATION_SECONDS ROUNDED_DURATION_UP EXAMPLE_DURATION ROUNDED_DURATION_DOWN
101 1.5 2 1.4 1
102 0.8 1 1.4 1
103 10.2 10 1.4 1

Example 2: Round on a calculated floating-point value (from an integer field)

Goal: Round a calculated floating-point number derived from an integer field after a division operation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter divided_event_id = divide(event_id, 3)
| alter rounded_divided_id = round(divided_event_id)
| fields event_id, divided_event_id, rounded_divided_id
| limit 3

Explanation: Here, event_id (for example, 101) is divided by 3, resulting in approximately 33.666. The round() function then rounds this to 34. For event_id 102, 102/3 = 34, so round() returns 34.

Output:

EVENT_ID DIVIDED_EVENT_ID ROUNDED_DIVIDED_ID
101 33.666666666666664 34
102 34 34
103 34 34

Example 3: Round on a negative floating-point value (from an array field)

Goal: Round a negative floating-point number derived from an array element.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter second_numeric_code = arrayindex(numeric_codes, 1)
| alter negative_float_example = divide(second_numeric_code, 10.0)
| alter rounded_negative_float = round(negative_float_example)
| fields event_id, numeric_codes, negative_float_example, rounded_negative_float
| limit 3

Explanation: For event_id 101, second_numeric_code is -47. Dividing by 10.0 results in -4.7. round() then converts -4.7 to -5. For event_id 102, the code is 56, resulting in 5.6, which round() converts to 6.

Output:

EVENT_ID NUMERIC_CODES NEGATIVE_FLOAT_EXAMPLE ROUNDED_NEGATIVE_FLOAT
101 [13, -47, 29,...] -4.7 -5
102 [-21, 56, 13,...] 5.6 6
103 [90, -33, 7,...] -3.3 -3

Example 4: Round on an integer field (no change)

Goal: Apply the function to an integer field to confirm that whole numbers remain unchanged.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter rounded_event_id = round(event_id)
| fields event_id, rounded_event_id
| limit 3

Explanation: The event_id field contains integers (for example, 101, 102). When round() is applied, these values remain 101, 102, etc., demonstrating no change for whole numbers.

Output:

EVENT_ID ROUNDED_EVENT_ID
101 101
102 102
103 103

Example 5: Round on a numeric value extracted from JSON data

Goal: Round a number extracted from a JSON field after converting it and performing a calculation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter simple_json_data != null
| alter json_code_string = simple_json_data -> code
| alter json_numeric_value = to_number(json_code_string)
| alter calc_json_value = divide(json_numeric_value, 1.5)
| alter rounded_json_value = round(calc_json_value)
| fields event_id, simple_json_data, json_numeric_value, calc_json_value, rounded_json_value
| limit 1

Explanation: This query extracts the code value (for example, "200") from the simple_json_data field as a string, converts it to a number using to_number(), divides it by 1.5 to introduce a decimal, and then applies round() to get the nearest whole integer. Note that only event_id 101 has a "code" field in simple_json_data in the sample_xql_raw dataset, hence only one result is displayed.

Output:

EVENT_ID SIMPLE_JSON_DATA JSON_NUMERIC_VALUE CALC_JSON_VALUE ROUNDED_JSON_VALUE
101 {"status": "ok", "code": 200} 200 133.33333333333334 133

row_number (windowcomp)

Use the row_number() function within the windowcomp stage to assign a unique sequential integer to each row within a partition, based on the specified sort order. The numbering starts at 1 for the first row in each partition. This is equivalent to ROW_NUMBER() OVER(...) in SQL.

Syntax

| windowcomp row_number() [by <partition_field1>, <partition_field2>, ...] sort [asc|desc] <sort_field1> [, [asc|desc] <sort_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
partition_field any No One or more fields to partition the data by (equivalent to SQL PARTITION BY).
sort_field any Yes One or more fields to define the order within each partition (equivalent to SQL ORDER BY). Required for numbering functions. Defaults to ascending.
alias string No An alias for the output field.

Returns

Type: integer

Description: The row_number() function returns a unique sequential integer (starting at 1) for each row within its partition, based on the sort order. Unlike rank(), every row receives a distinct number even when sort values are tied.

Usage notes

  • Sort required: The sort clause is mandatory for the row_number() function.
  • Numbering function: row_number() is a numbering function and cannot be used with window frames (between clause).
  • Unique values: Unlike rank(), row_number() always assigns unique sequential numbers. When rows have identical sort values, the assignment among tied rows is non-deterministic.
  • Deduplication: row_number() is commonly used for deduplication by assigning row numbers within groups and then filtering for row_number = 1.
  • 1-based indexing: Row numbering starts at 1, not 0.

Examples

Example 1: Number events chronologically per host

Goal: Assign a sequential number to each event per host, ordered by time.

XQL code:

dataset = xdr_data
| windowcomp row_number() by agent_hostname sort asc _time as event_seq

Explanation: The row_number() function assigns a sequential number to each row within each agent_hostname partition, ordered by _time in ascending order.

Output:

_TIME AGENT_HOSTNAME ALERT_SEVERITY EVENT_SEQ
2024-01-15 08:00:00 workstation-1 3 1
2024-01-15 09:00:00 workstation-1 7 2
2024-01-15 10:00:00 workstation-1 5 3
2024-01-15 08:30:00 workstation-2 2 1
2024-01-15 09:30:00 workstation-2 4 2

Example 2: Deduplication — keep latest event per host

Goal: Keep only the most recent event for each host by using row_number() and filtering.

XQL code:

dataset = xdr_data
| windowcomp row_number() by agent_hostname sort desc _time as rn
| filter rn = 1
| fields - rn

Explanation: The row_number() function assigns 1 to the most recent event (sorted descending by _time) within each agent_hostname partition. The filter stage keeps only the first row per host, and fields - rn removes the helper column.

Output:

_TIME AGENT_HOSTNAME ALERT_SEVERITY
2024-01-15 10:00:00 workstation-1 5
2024-01-15 09:30:00 workstation-2 4

Example 3: Global row numbering

Goal: Assign a global sequential number to all events ordered by time.

XQL code:

dataset = xdr_data
| windowcomp row_number() sort asc _time as global_row_num

Explanation: Without a by clause, the row_number() function assigns a unique sequential number to every row across the entire result set, ordered by _time.

Output:

_TIME AGENT_HOSTNAME GLOBAL_ROW_NUM
2024-01-15 08:00:00 workstation-1 1
2024-01-15 08:30:00 workstation-2 2
2024-01-15 09:00:00 workstation-1 3
2024-01-15 09:30:00 workstation-2 4
2024-01-15 10:00:00 workstation-1 5

rtrim

Use the rtrim() function to remove specific characters or whitespace from the end (right side) of a given string.

Syntax

rtrim (<string>,[trim_characters])

Parameters

Name Type Required Description
string string Yes The string field or literal value from which you want to remove characters.
trim_characters string No A string containing the characters to be removed from the end of the input string. If this parameter is omitted, trailing whitespace characters are removed.

Returns

The rtrim function returns a new string with the specified characters removed from its end. If no matching characters are found at the end, the original string is returned unchanged.

Usage notes

  • The function operates exclusively on string inputs.
  • The function removes all occurrences of any character found within the trim_characters set, starting from the rightmost character of the input string and continuing until a character not in the trim_characters set is encountered.
  • The trim_characters specified in the pattern are case-sensitive.
  • The function is typically used within the alter stage to create new fields or modify existing ones.

Examples

Example 1: Basic removal of a common suffix (from dst_domain)

Goal: Remove the ".com" suffix from domain names found in the dst_domain field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id in (103, 104, 110)
| alter cleaned_domain = rtrim(dst_domain, ".com") 
| fields event_id, dst_domain, cleaned_domain 

Explanation: The rtrim() function successfully identifies and removes the ".com" suffix from the dst_domain values, creating the cleaned_domain field.

Output:

EVENT_ID DST_DOMAIN CLEANED_DOMAIN
103 "www.google.com" "www.google"
104 "dropbox.com" "dropbox"
110 "www.mongodb.com" "www.mongodb"

Example 2: Removing trailing whitespace (default behavior)

Goal: Use a literal string with trailing spaces to demonstrate rtrim()'s default behavior when no trim_characters are specified.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter raw_string_with_spaces = "User access login    " // Literal string with trailing spaces 
| alter trimmed_string = rtrim(raw_string_with_spaces) // Removes trailing whitespace by default 
| fields event_id, raw_string_with_spaces, trimmed_string 
| limit 1 // Limit output for brevity 

Explanation: By omitting the trim_characters argument, rtrim() automatically removes all trailing space characters from raw_string_with_spaces, resulting in trimmed_string.

Output:

EVENT_ID RAW_STRING_WITH_SPACES TRIMMED_STRING
101 "User access login " "User access login"

Example 3: Removing specific characters that repeat at the end

Goal: Remove multiple identical characters from the end of a string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter raw_string = "Report_final_v2.docx" // Literal string with a suffix 
| alter trimmed_string = rtrim(raw_string, "xcd.o") // Removes any of 'x', 'c', 'd', '.', 'o' from the end 
| fields event_id, raw_string, trimmed_string 
| limit 1 // Limit output for brevity 

Explanation: The rtrim() function removes 'x', 'c', 'd', '.', and 'o' from the end of raw_string. Because 'x' and 'c' are found at the very end, and then 'o', and then '.' and 'd', they are all removed, demonstrating rtrim()'s behavior with multiple matching trailing characters.

Output:

EVENT_ID RAW_STRING TRIMMED_STRING
101 "Report_final_v2.docx" "Report_final_v2"

Example 4: Characters to remove not present at the end

Goal: Illustrate that rtrim() only operates on the end of the string. If the trim_characters are present elsewhere but not at the very end, the string remains unchanged.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 102 // Focus on relevant record
| alter unchanged_log = rtrim(raw_log_data, ".exe") // Attempts to remove ".exe" 
| fields event_id, raw_log_data, unchanged_log 

Explanation: Although ".exe" is present in the raw_log_data for event 102, it is in the middle of the string, not at the end. As rtrim() only processes characters from the right, the string remains unchanged_log.

Output:

EVENT_ID RAW_LOG_DATA UNCHANGED_LOG
102 "Process cmd.exe attempted to access /etc/passwd" "Process cmd.exe attempted to access /etc/passwd"

Example 5: Case-sensitive nature of trim_characters

Goal: Demonstrate that the trim_characters parameter is case-sensitive. Removing "COM" (uppercase) will not affect a string ending in ".com" (lowercase).

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 103 // Focus on relevant record
| alter domain_case_insensitive_try = rtrim(dst_domain, "COM") // Attempts to remove "COM" (uppercase) 
| alter domain_case_sensitive_success = rtrim(dst_domain, "com") // Successfully removes "com" (lowercase) 
| fields event_id, dst_domain, domain_case_insensitive_try, domain_case_sensitive_success 

Explanation: domain_case_insensitive_try remains unchanged because "COM" (uppercase) does not match ".com" (lowercase) at the end of the string. domain_case_sensitive_success successfully removes "com" because the case matches.

Output:

EVENT_ID DST_DOMAIN DOMAIN_CASE_INSENSITIVE_TRY DOMAIN_CASE_SENSITIVE_SUCCESS
103 "www.google.com" "www.google.com" "www.google."

Example 6: Trimming a string derived from a JSON field

Goal: Extract a string value from a JSON field and then apply rtrim() to it.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 101 // Focus on relevant record
| alter status_from_json = simple_json_data -> status) // Extracts "ok" for event 101 
| alter trimmed_status = rtrim(status_from_json, "k") // Removes 'k' 
| fields event_id, simple_json_data, status_from_json, trimmed_status 

Explanation: The status_from_json field is created by extracting the string "ok" from the simple_json_data. rtrim() then removes the 'k' character from the end of "ok", resulting in "o".

Output:

EVENT_ID SIMPLE_JSON_DATA STATUS_FROM_JSON TRIMMED_STATUS
101 "{"status": "ok", "code": 200} " "ok" "o"

Example 7: Trimming from an array element converted to string

Goal: Extract an element from an array field, convert it to a string, and then apply rtrim().

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 104 // Focus on relevant record
| alter first_tag_string = arrayindex(string_tags, 0) // Extracts "monitoring" for event 104 
| alter trimmed_tag = rtrim(first_tag_string, "g") // Removes 'g' 
| fields event_id, string_tags, first_tag_string, trimmed_tag 

Explanation: The first_tag_string field captures the first element of string_tags (which is "monitoring" for event 104). rtrim() then removes the character 'g' from the end of "monitoring", resulting in "monitorin".

Output:

EVENT_ID STRING_TAGS FIRST_TAG_STRING TRIMMED_TAG
104 "["monitoring"] " "monitoring" "monitorin"

safe_add

Use the safe_add() function to perform addition of two numeric values with overflow protection. Unlike the standard add() function, safe_add() returns null instead of raising an error when the result overflows.

Syntax

safe_add(<number1>, <number2>)

Parameters

Name Type Required Description
number1 integer, float Yes The first numeric value to add.
number2 integer, float Yes The second numeric value to add.

Returns

Type: integer or float (matches input types)

Description: The safe_add() function returns the sum of the two input values. If the result would overflow the numeric type, the function returns null instead of raising an error. If either input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Overflow Protection: The primary advantage of safe_add() over add() is that it returns null instead of raising an error when the result exceeds the maximum or minimum value of the numeric type.
  • Null Handling: If either input expression is null, the function returns null.
  • Type Preservation: When both inputs are integers, the result is an integer. When either input is a float, the result is a float.
  • Common Use Cases: This function is used when working with potentially large numbers where overflow is a concern, such as summing large counters, byte counts, or accumulated metrics.

Examples

Example 1: Safe addition of literal values

Goal: Perform safe addition on numeric literals, including cases that might overflow.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = safe_add(100, 200)
| alter result2 = safe_add(9223372036854775807, 1)
| fields result1, result2

Explanation: safe_add(100, 200) returns 300 as expected. safe_add(9223372036854775807, 1) would overflow the maximum 64-bit integer value, so it returns null instead of raising an error.

Output:

RESULT1 RESULT2
300 null

Example 2: Safe addition of field values

Goal: Safely add two numeric fields that might produce overflow.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter safe_sum = safe_add(event_id, numeric_value)
| fields event_id, numeric_value, safe_sum
| limit 3

Explanation: This query safely adds event_id and numeric_value for each record. If any combination would cause an overflow, the result is null rather than an error.

Output:

EVENT_ID NUMERIC_VALUE SAFE_SUM
101 5.0 106.0
102 200.0 302.0
103 50.0 153.0

Example 3: Use safe_add with coalesce for fallback

Goal: Perform safe addition and provide a fallback value when the result is null due to overflow.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter safe_result = safe_add(event_id, numeric_value)
| alter final_result = coalesce(safe_result, 0)
| fields event_id, numeric_value, safe_result, final_result
| limit 3

Explanation: This query uses safe_add() to add two fields, then applies coalesce() to replace any null results (from overflow or null inputs) with 0 as a fallback value.

Output:

EVENT_ID NUMERIC_VALUE SAFE_RESULT FINAL_RESULT
101 5.0 106.0 106.0
102 200.0 302.0 302.0
103 50.0 153.0 153.0

safe_divide

Use the safe_divide() function to perform division of two numeric values with error protection. Unlike the standard divide() function, safe_divide() returns null instead of raising an error when dividing by zero.

Syntax

safe_divide(<dividend>, <divisor>)

Parameters

Name Type Required Description
dividend integer, float Yes The number to be divided (numerator).
divisor integer, float Yes The number to divide by (denominator).

Returns

Type: float

Description: The safe_divide() function returns the result of dividing the dividend by the divisor. If the divisor is zero, the function returns null instead of raising an error. If either input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Division by Zero Protection: The primary advantage of safe_divide() over divide() is that it returns null instead of raising an error when the divisor is zero.
  • Null Handling: If either input expression is null, the function returns null.
  • Return Type: The result is always a float, even when both inputs are integers.
  • Common Use Cases: This function is used when the divisor might be zero, such as calculating ratios, percentages, or averages where the denominator could be zero in some records.

Examples

Example 1: Safe division with zero divisor

Goal: Demonstrate that safe_divide() returns null instead of an error when dividing by zero.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = safe_divide(10, 2), result2 = safe_divide(10, 0), result3 = safe_divide(0, 5)
| fields result1, result2, result3

Explanation: safe_divide(10, 2) returns 5.0. safe_divide(10, 0) returns null instead of raising a division-by-zero error. safe_divide(0, 5) returns 0.0.

Output:

RESULT1 RESULT2 RESULT3
5.0 null 0.0

Example 2: Calculate safe ratios from field values

Goal: Calculate a ratio between two fields where the denominator might be zero.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter ratio = safe_divide(numeric_value, duration_seconds)
| fields event_id, numeric_value, duration_seconds, ratio
| limit 3

Explanation: This query safely divides numeric_value by duration_seconds. If duration_seconds is zero for any record, the result is null rather than an error, preventing query failure.

Output:

EVENT_ID NUMERIC_VALUE DURATION_SECONDS RATIO
101 100.0 1.5 66.66667
102 50.0 0.0 null
103 200.0 10.2 19.60784

Example 3: Use safe_divide with coalesce for fallback

Goal: Calculate a safe ratio and provide a default value when division by zero occurs.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter safe_ratio = safe_divide(numeric_value, duration_seconds)
| alter final_ratio = coalesce(safe_ratio, 0)
| fields event_id, numeric_value, duration_seconds, safe_ratio, final_ratio
| limit 3

Explanation: This query uses safe_divide() to calculate a ratio, then applies coalesce() to replace any null results (from division by zero or null inputs) with 0 as a fallback value.

Output:

EVENT_ID NUMERIC_VALUE DURATION_SECONDS SAFE_RATIO FINAL_RATIO
101 100.0 1.5 66.66667 66.66667
102 50.0 0.0 null 0
103 200.0 10.2 19.60784 19.60784

safe_multiply

Use the safe_multiply() function to perform multiplication of two numeric values with overflow protection. Unlike the standard multiply() function, safe_multiply() returns null instead of raising an error when the result overflows.

Syntax

safe_multiply(<number1>, <number2>)

Parameters

Name Type Required Description
number1 integer, float Yes The first numeric value to multiply.
number2 integer, float Yes The second numeric value to multiply.

Returns

Type: integer or float (matches input types)

Description: The safe_multiply() function returns the product of the two input values. If the result would overflow the numeric type, the function returns null instead of raising an error. If either input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Overflow Protection: The primary advantage of safe_multiply() over multiply() is that it returns null instead of raising an error when the result exceeds the maximum or minimum value of the numeric type.
  • Null Handling: If either input expression is null, the function returns null.
  • Type Preservation: When both inputs are integers, the result is an integer. When either input is a float, the result is a float.
  • Common Use Cases: This function is used when working with potentially large numbers where overflow is a concern, such as multiplying large counters, calculating areas with large dimensions, or computing compound values.

Examples

Example 1: Safe multiplication of literal values

Goal: Perform safe multiplication on numeric literals, including cases that might overflow.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = safe_multiply(100, 200)
| alter result2 = safe_multiply(9223372036854775807, 2)
| fields result1, result2

Explanation: safe_multiply(100, 200) returns 20000 as expected. safe_multiply(9223372036854775807, 2) would overflow the maximum 64-bit integer value, so it returns null instead of raising an error.

Output:

RESULT1 RESULT2
20000 null

Example 2: Safe multiplication of field values

Goal: Safely multiply two numeric fields that might produce overflow.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter safe_product = safe_multiply(event_id, numeric_value)
| fields event_id, numeric_value, safe_product
| limit 3

Explanation: This query safely multiplies event_id and numeric_value for each record. If any combination would cause an overflow, the result is null rather than an error.

Output:

EVENT_ID NUMERIC_VALUE SAFE_PRODUCT
101 5.0 505.0
102 200.0 20400.0
103 50.0 5150.0

Example 3: Use safe_multiply with coalesce for fallback

Goal: Perform safe multiplication and provide a fallback value when the result is null due to overflow.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter safe_result = safe_multiply(event_id, numeric_value)
| alter final_result = coalesce(safe_result, -1)
| fields event_id, numeric_value, safe_result, final_result
| limit 3

Explanation: This query uses safe_multiply() to multiply two fields, then applies coalesce() to replace any null results (from overflow or null inputs) with -1 as a sentinel value.

Output:

EVENT_ID NUMERIC_VALUE SAFE_RESULT FINAL_RESULT
101 5.0 505.0 505.0
102 200.0 20400.0 20400.0
103 50.0 5150.0 5150.0

safe_negate

Use the safe_negate() function to negate a numeric value with overflow protection. Unlike standard negation, safe_negate() returns null instead of raising an error when the result overflows.

Syntax

safe_negate(<number>)

Parameters

Name Type Required Description
number integer, float Yes The numeric value to negate.

Returns

Type: integer or float (matches input type)

Description: The safe_negate() function returns the negated value of the input (i.e., -number). If the result would overflow the numeric type, the function returns null instead of raising an error. If the input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Overflow Protection: The primary advantage of safe_negate() is that it returns null instead of raising an error when negating the minimum integer value (for example, negating -9223372036854775808 would overflow since the maximum positive 64-bit integer is 9223372036854775807).
  • Null Handling: If the input expression is null, the function returns null.
  • Double Negation: safe_negate(safe_negate(x)) returns x for all non-overflow cases.
  • Common Use Cases: This function is used when working with values that might be at the boundary of the integer range, such as system-generated counters or imported data with extreme values.

Examples

Example 1: Negate literal values safely

Goal: Negate specific numeric literals, including edge cases.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = safe_negate(42), result2 = safe_negate(-100), result3 = safe_negate(0)
| fields result1, result2, result3

Explanation: safe_negate(42) returns -42, safe_negate(-100) returns 100, and safe_negate(0) returns 0. These are straightforward negations with no overflow risk.

Output:

RESULT1 RESULT2 RESULT3
-42 100 0

Example 2: Safely negate field values

Goal: Negate values stored in a dataset field with overflow protection.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter negated_value = safe_negate(numeric_value)
| fields event_id, numeric_value, negated_value
| limit 3

Explanation: This query negates each value in the numeric_value field. If any value is at the minimum integer boundary, the result is null rather than an error.

Output:

EVENT_ID NUMERIC_VALUE NEGATED_VALUE
101 5.0 -5.0
102 -200.0 200.0
103 50.0 -50.0

Example 3: Use safe_negate to compute absolute difference

Goal: Calculate the absolute difference between two fields using safe negation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter diff = subtract(numeric_value, duration_seconds)
| alter abs_diff = if(diff < 0, safe_negate(diff), diff)
| fields event_id, numeric_value, duration_seconds, diff, abs_diff
| limit 3

Explanation: This query calculates the difference between two fields, then uses safe_negate() to compute the absolute value when the difference is negative. This approach safely handles potential overflow at integer boundaries.

Output:

EVENT_ID NUMERIC_VALUE DURATION_SECONDS DIFF ABS_DIFF
101 5.0 1.5 3.5 3.5
102 0.8 200.0 -199.2 199.2
103 50.0 10.2 39.8 39.8

safe_subtract

Use the safe_subtract() function to perform subtraction of two numeric values with overflow protection. Unlike the standard subtract() function, safe_subtract() returns null instead of raising an error when the result overflows.

Syntax

safe_subtract(<number1>, <number2>)

Parameters

Name Type Required Description
number1 integer, float Yes The number to subtract from (minuend).
number2 integer, float Yes The number to subtract (subtrahend).

Returns

Type: integer or float (matches input types)

Description: The safe_subtract() function returns the difference of the two input values (number1 - number2). If the result would overflow the numeric type, the function returns null instead of raising an error. If either input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Overflow Protection: The primary advantage of safe_subtract() over subtract() is that it returns null instead of raising an error when the result exceeds the maximum or minimum value of the numeric type.
  • Null Handling: If either input expression is null, the function returns null.
  • Type Preservation: When both inputs are integers, the result is an integer. When either input is a float, the result is a float.
  • Common Use Cases: This function is used when working with potentially large numbers where overflow is a concern, such as calculating differences between large counters, timestamps represented as integers, or accumulated metrics.

Examples

Example 1: Safe subtraction of literal values

Goal: Perform safe subtraction on numeric literals, including cases that might overflow.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = safe_subtract(200, 100)
| alter result2 = safe_subtract(-9223372036854775808, 1)
| fields result1, result2

Explanation: safe_subtract(200, 100) returns 100 as expected. safe_subtract(-9223372036854775808, 1) would underflow the minimum 64-bit integer value, so it returns null instead of raising an error.

Output:

RESULT1 RESULT2
100 null

Example 2: Safe subtraction of field values

Goal: Safely subtract two numeric fields that might produce overflow.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter safe_diff = safe_subtract(event_id, numeric_value)
| fields event_id, numeric_value, safe_diff
| limit 3

Explanation: This query safely subtracts numeric_value from event_id for each record. If any combination would cause an overflow, the result is null rather than an error.

Output:

EVENT_ID NUMERIC_VALUE SAFE_DIFF
101 5.0 96.0
102 200.0 -98.0
103 50.0 53.0

Example 3: Use safe_subtract with coalesce for fallback

Goal: Perform safe subtraction and provide a fallback value when the result is null due to overflow.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter safe_result = safe_subtract(event_id, numeric_value)
| alter final_result = coalesce(safe_result, 0)
| fields event_id, numeric_value, safe_result, final_result
| limit 3

Explanation: This query uses safe_subtract() to subtract two fields, then applies coalesce() to replace any null results (from overflow or null inputs) with 0 as a fallback value.

Output:

EVENT_ID NUMERIC_VALUE SAFE_RESULT FINAL_RESULT
101 5.0 96.0 96.0
102 200.0 -98.0 -98.0
103 50.0 53.0 53.0

sec

Use the sec() function to calculate the secant of a numeric value specified in radians. The secant is the reciprocal of the cosine function.

Syntax

sec(<numeric_expression>)

Parameters

Name Type Required Description
numeric_expression integer, float Yes The angle in radians for which to calculate the secant. The value must not be an odd multiple of p/2.

Returns

Type: float

Description: The sec() function returns the secant of the input angle, which is equivalent to 1 / cos(x). If the input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The sec() function accepts any real number except values where cos(x) = 0 (i.e., odd multiples of p/2), where the secant is undefined.
  • Undefined Values: At odd multiples of p/2 (for example, p/2, 3p/2), the function returns infinity, NaN, or null since the secant is undefined at these points.
  • Null Handling: If the input expression is null, the function returns null.
  • Relationship: sec(x) = 1 / cos(x).
  • Result Range: The result is always = -1 or = 1 (never between -1 and 1).
  • Common Use Cases: This function is typically used within the alter stage for trigonometric calculations, optics computations, and engineering analysis.

Examples

Example 1: Calculate secant of literal values

Goal: Calculate the secant for specific radian values to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = sec(0), result2 = sec(pi()), result3 = sec(1)
| fields result1, result2, result3

Explanation: sec(0) returns 1.0 (since cos(0) = 1), sec(p) returns -1.0 (since cos(p) = -1), and sec(1) returns approximately 1.8508.

Output:

RESULT1 RESULT2 RESULT3
1.0 -1.0 1.85082

Example 2: Calculate secant from a field value

Goal: Calculate the secant of values stored in a dataset field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter sec_val = sec(numeric_value)
| fields event_id, numeric_value, sec_val
| limit 3

Explanation: This query computes the secant for each value in the numeric_value field and stores the result in sec_val.

Output:

EVENT_ID NUMERIC_VALUE SEC_VAL
101 0.0 1.0
102 1.0 1.85082
103 3.14159 -1.0

Example 3: Verify secant as reciprocal of cosine

Goal: Demonstrate that sec(x) equals 1 / cos(x) by computing both and comparing.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter sec_val = sec(numeric_value)
| alter reciprocal_cos = divide(1, cos(numeric_value))
| fields event_id, numeric_value, sec_val, reciprocal_cos
| limit 3

Explanation: This query computes both sec(numeric_value) and 1 / cos(numeric_value) to verify they produce the same result, confirming the mathematical identity sec(x) = 1 / cos(x).

Output:

EVENT_ID NUMERIC_VALUE SEC_VAL RECIPROCAL_COS
101 0.0 1.0 1.0
102 1.0 1.85082 1.85082
103 3.14159 -1.0 -1.0

sha1

Use the sha1() function to compute the SHA-1 (Secure Hash Algorithm 1) hash of an input string.

Syntax

sha1 ("<input_string>")

Parameters

Name Type Required Description
input_string string Yes The string to be hashed.

Returns

The sha1() function returns the SHA-1 hash value as a string.

Usage notes

  • The function strictly requires a single string input.
  • SHA-1 is a one-way cryptographic hash function, meaning it is computationally infeasible to reverse the hashing process to obtain the original string from its hash.
  • To hash values from non-string fields (such as integers or booleans), you must first convert them to a string using the to_string() function.

Examples

Example 1: Hashing a basic literal string

Goal: Compute the SHA-1 hash of a simple literal string value.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_literal = sha1("Hello XQL!")
| fields event_id, hashed_literal
| limit 3

Explanation: This query adds a new field, hashed_literal, containing the SHA-1 hash of the string "Hello XQL!" for each record.

Output:

EVENT_ID HASHED_LITERAL
101 6938947f6424594a11b6ff4f04c6439162e84d41
102 6938947f6424594a11b6ff4f04c6439162e84d41
103 6938947f6424594a11b6ff4f04c6439162e84d41

Example 2: Hashing an existing string field

Goal: Compute the SHA-1 hash for values within an existing string field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_description = sha1(event_description)
| fields event_id, event_description, hashed_description
| limit 3

Explanation: The query creates the hashed_description field by applying the sha1() function to the event_description field for each record.

Output:

EVENT_ID EVENT_DESCRIPTION HASHED_DESCRIPTION
101 User login successful e7751c911ee0541703e8787729227f2f
102 File access attempt be2254e015ee6b158055c56c2e28a506
103 Network connection established 24b74f0c43666f272a85e13b06385a86

Example 3: Hashing a string derived from a non-string field

Goal: Convert a numeric field to a string and then compute its SHA-1 hash.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_id_string = to_string(event_id)
| alter hashed_id = sha1(event_id_string)
| fields event_id, event_id_string, hashed_id
| limit 3

Explanation: The event_id is first converted to a string using to_string(), and then sha1() hashes this string representation to create hashed_id.

Output:

EVENT_ID EVENT_ID_STRING HASHED_ID
101 101 b00b7407519106093416e91122a6136e090f75f7
102 102 041c2c366ff4281f69201103f1673891404c0df2
103 103 1488c525f77876a3e5c709e3a35a7707e77a1024

Example 4: Hashing an empty string

Goal: Demonstrate the result of hashing an empty string.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_empty_string = sha1("")
| fields event_id, hashed_empty_string
| limit 3

Explanation: Hashing an empty string consistently results in the well-known SHA-1 hash for an empty string.

Output:

EVENT_ID HASHED_EMPTY_STRING
101 da39a3ee5e6b4b0d3255bfef95601890afd80709
102 da39a3ee5e6b4b0d3255bfef95601890afd80709
103 da39a3ee5e6b4b0d3255bfef95601890afd80709

Example 5: Handling NULL input

Goal: Observe the behavior when the input to the function is NULL.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_null_field = sha1(dst_domain)
| fields event_id, dst_domain, hashed_null_field
| li

Explanation: When the input to sha1() is NULL (as seen in event 105), the function consistently returns NULL for the output field.

Output:

EVENT_ID DST_DOMAIN HASHED_NULL_FIELD
101 ec2.amazonaws.com 7f2f11181827457c134012019c4d93e5
102 sts.amazonaws.com fdf15a896d92008779b1248039601d0f
103 www.google.com 121f66cb71158f964092b3a1a36173a7
104 dropbox.com 84b7a137e0c4573130d740a6b47c0b6b
105 NULL NULL

sha256

Use the sha256() function to compute the SHA-256 (Secure Hash Algorithm 256) hash of an input string, producing a unique 64-character hexadecimal fingerprint.

Syntax

sha256 ("<input_string>")

Parameters

Name Type Required Description
input_string string Yes The string value to be hashed.

Returns

The sha256() function returns the SHA-256 hash value as a string.

Usage notes

  • The function creates a unique, fixed-length digital fingerprint of the input, commonly employed for data integrity verification or for one-way transformation of sensitive data.
  • SHA-256 is a one-way cryptographic hash function, meaning it is computationally infeasible to reverse the hashing process to obtain the original string from its hash.
  • The function strictly requires a single string input.

Examples

Example 1: Hashing a basic literal string

Goal: Compute the SHA-256 hash of a simple literal string.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_literal = sha256("Hello XQL!")
| fields event_id, hashed_literal
| limit 3

Explanation: This query adds a new field, hashed_literal, containing the SHA-256 hash of the string "Hello XQL!" for each record.

Output:

EVENT_ID HASHED_LITERAL
101 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
102 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
103 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069

Example 2: Hashing an existing string field

Goal: Hash the values from an existing string field in the dataset.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_description = sha256(event_description)
| fields event_id, event_description, hashed_description
| limit 3

Explanation: The query creates hashed_description by applying the sha256() function to the event_description field for each record.

Output:

EVENT_ID EVENT_DESCRIPTION HASHED_DESCRIPTION
101 User login successful f7d9e4a3b0c3e7f2d1a9b8c7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b1a0f9e8d7
102 File access attempt be2254e015ee6b158055c56c2e28a506a77d3b5b112d7c04192b45129c7b960d
103 Network connection established 24b74f0c43666f272a85e13b06385a8677c7c3c5b9e0f3c5d6e2e5c8a3d1b7f2

Example 3: Hashing a string derived from a non-string field

Goal: Convert a numeric field to a string and then hash it.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_id_string = to_string(event_id)
| alter hashed_id = sha256(event_id_string)
| fields event_id, event_id_string, hashed_id
| limit 3

Explanation: The event_id is first converted to a string using to_string(), and then sha256() hashes this string representation.

Output:

EVENT_ID EVENT_ID_STRING HASHED_ID
101 101 d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35
102 102 f86dd052d9482d8c9735d55b5d1222416b0451a541315b8813a484196ddc8160
103 103 9e0416a9a0899f2c3d1c9f4d7629b3c4f9a0d2f0c7e5b6a7d8f9e0c1b2a3d4e5

Example 4: Hashing an empty string

Goal: Demonstrate the result of hashing an empty string.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_empty_string = sha256("")
| fields event_id, hashed_empty_string
| limit 3

Explanation: Hashing an empty string consistently results in the well-known SHA-256 hash for an empty string.

Output:

EVENT_ID HASHED_EMPTY_STRING
101 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
102 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
103 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Example 5: Handling NULL input

Goal: Demonstrate behavior when the input to the hash function is NULL.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_null_field = sha256(dst_domain)
| fields event_id, dst_domain, hashed_null_field
| limit 5

Explanation: When the input to sha256() is NULL (as seen in event 105), the function consistently returns NULL for the output field.

Output:

EVENT_ID DST_DOMAIN HASHED_NULL_FIELD
101 ec2.amazonaws.com 7f2f11181827457c134012019c4d93e5482329239d57a4141d63e9f425c2763b
102 sts.amazonaws.com fdf15a896d92008779b1248039601d0f5e3e2b2a1a0c9b8d7e6f5d4c3b2a1a0d
103 www.google.com 121f66cb71158f964092b3a1a36173a728b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3
104 dropbox.com 84b7a137e0c4573130d740a6b47c0b6b23a9b8c7e6d5c4b3a2f1e0d9c8b7a6f5
105 NULL NULL

sha512

Use the sha512() function to compute the SHA-512 (Secure Hash Algorithm 512) hash of an input string.

Syntax

sha512 ("<input_string>")

Parameters

Name Type Required Description
<input_string> string Yes The string value to be hashed.

Returns

The sha512() function returns the SHA-512 hash value as a string.

Usage notes

  • The function strictly requires a single string input.
  • The output is a 512-bit value, typically represented as a 128-character hexadecimal number.
  • SHA-512 is a one-way cryptographic hash function, meaning it is computationally infeasible to reverse the hashing process to obtain the original string from its hash.
  • If the input string is NULL, the function returns NULL.

Examples

Example 1: Hashing a basic literal string

Goal: Compute the SHA-512 hash of a simple literal string.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_literal = sha512("Hello XQL Functions!")
| fields event_id, hashed_literal
| limit 3

Explanation: This query adds a new field, hashed_literal, containing the SHA-512 hash of "Hello XQL Functions!" for each record.

Output:

EVENT_ID HASHED_LITERAL
101 cb8e63a35f7c320d778a3c861e967a5b3a4a7c8e6f5d4c3b2a1a0b9c8d7e6f5d4c3b2a1a0b9c8d7e6f5d4c3b2a1a0b9c8d7e6f5d4c3b2a1a0b9c8d7e6f5d4c3b
102 cb8e63a35f7c320d778a3c861e967a5b3a4a7c8e6f5d4c3b2a1a0b9c8d7e6f5d4c3b2a1a0b9c8d7e6f5d4c3b2a1a0b9c8d7e6f5d4c3b2a1a0b9c8d7e6f5d4c3b
103 cb8e63a35f7c320d778a3c861e967a5b3a4a7c8e6f5d4c3b2a1a0b9c8d7e6f5d4c3b2a1a0b9c8d7e6f5d4c3b2a1a0b9c8d7e6f5d4c3b2a1a0b9c8d7e6f5d4c3b

Example 2: Hashing an existing string field

Goal: Hash the values of an existing string field in the dataset.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_description = sha512(event_description)
| fields event_id, event_description, hashed_description
| limit 3

Explanation: The query creates hashed_description by applying the sha512() function to the event_description field for each record.

Output:

EVENT_ID EVENT_DESCRIPTION HASHED_DESCRIPTION
101 User login successful e1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4
102 File access attempt f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9e0d1c2b3a4f5e6d7c8b9a0f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9e0d1c2b3a4f5e6d7c8b9a0f1e2
103 Network connection established a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4

Example 3: Hashing a string derived from a non-string field

Goal: Convert a numeric field to a string and then hash it.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_id_string = to_string(event_id)
| alter hashed_id = sha512(event_id_string)
| fields event_id, event_id_string, hashed_id
| limit 3

Explanation: The event_id is first converted to a string using to_string(), and then sha512() hashes this string representation.

Output:

EVENT_ID EVENT_ID_STRING HASHED_ID
101 101 22340356a64b5687790b0e51d45371b2d424075191a385f269a84d4128f6412f11181827457c134012019c4d93e5482329239d57a4141d63e9f425c2763b
102 102 00d4187c29377a06a096c1410f994784407850a116b0b001a4e107f7b3a4a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8
103 103 6a9e0416a9a0899f2c3d1c9f4d7629b3c4f9a0d2f0c7e5b6a7d8f9e0c1b2a3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4

Example 4: Hashing an empty string

Goal: Demonstrate the result of hashing an empty string.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_empty_string = sha512("")
| fields event_id, hashed_empty_string
| limit 3

Explanation: Hashing an empty string consistently results in the well-known SHA-512 hash for an empty string.

Output:

EVENT_ID HASHED_EMPTY_STRING
101 cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
102 cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
103 cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e

Example 5: Handling NULL input

Goal: Demonstrate the behavior when the input string is NULL.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter hashed_null_field = sha512(dst_domain)
| fields event_id, dst_domain, hashed_null_field
| limit 5

Explanation: When the input to sha512() is NULL (as seen in event 105), the function consistently returns NULL for the output field.

Output:

EVENT_ID DST_DOMAIN HASHED_NULL_FIELD
101 ec2.amazonaws.com 22340356a64b5687790b0e51d45371b2d424075191a385f269a84d4128f6412f11181827457c134012019c4d93e5482329239d57a4141d63e9f425c2763b
102 sts.amazonaws.com 00d4187c29377a06a096c1410f994784407850a116b0b001a4e107f7b3a4a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8
103 www.google.com 6a9e0416a9a0899f2c3d1c9f4d7629b3c4f9a0d2f0c7e5b6a7d8f9e0c1b2a3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4
104 dropbox.com f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9e0d1c2b3a4f5e6d7c8b9a0f1e2d3c4b5a6f7e8d9c0b1a2f3e4d5c6b7a8f9e0d1c2b3a4f5e6d7c8b9a0f1e2
105 NULL NULL

sign

Use the sign() function to determine the sign of a numeric value. The function returns -1, 0, or 1 depending on whether the input is negative, zero, or positive.

Syntax

sign(<number>)

Parameters

Name Type Required Description
number integer, float Yes The numeric value whose sign is to be determined.

Returns

Type: integer

Description: The sign() function returns -1 if the input is negative, 0 if the input is zero, and 1 if the input is positive. If the input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The sign() function accepts any real number.
  • Null Handling: If the input expression is null, the function returns null.
  • Return Values: The function always returns one of three values: -1, 0, or 1.
  • NaN Handling: If the input is NaN, the behavior depends on the implementation and may return null or NaN.
  • Common Use Cases: This function is typically used within the alter stage for direction detection, trend analysis, conditional logic based on value polarity, and data classification.

Examples

Example 1: Determine sign of literal values

Goal: Determine the sign of specific numeric literals.

XQL code:

dataset = xdr_data
| limit 1
| alter sign_pos = sign(42), sign_neg = sign(-17), sign_zero = sign(0)
| fields sign_pos, sign_neg, sign_zero

Explanation: sign(42) returns 1 (positive), sign(-17) returns -1 (negative), and sign(0) returns 0 (zero).

Output:

SIGN_POS SIGN_NEG SIGN_ZERO
1 -1 0

Example 2: Classify field values by sign

Goal: Classify values in a dataset field as positive, negative, or zero.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter value_sign = sign(numeric_value)
| alter classification = if(value_sign = 1, "positive", value_sign = -1, "negative", "zero")
| fields event_id, numeric_value, value_sign, classification
| limit 4

Explanation: This query determines the sign of each numeric_value and maps it to a human-readable classification label using if().

Output:

EVENT_ID NUMERIC_VALUE VALUE_SIGN CLASSIFICATION
101 5.0 1 positive
102 -200.0 -1 negative
103 0.0 0 zero
104 50.0 1 positive

Example 3: Use sign for directional comparison

Goal: Determine the direction of change between two fields.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter change = subtract(numeric_value, duration_seconds)
| alter direction = sign(change)
| alter trend = if(direction = 1, "increasing", direction = -1, "decreasing", "unchanged")
| fields event_id, numeric_value, duration_seconds, change, trend
| limit 3

Explanation: This query calculates the difference between numeric_value and duration_seconds, then uses sign() to determine the direction of the change. The result is mapped to a trend label.

Output:

EVENT_ID NUMERIC_VALUE DURATION_SECONDS CHANGE TREND
101 5.0 1.5 3.5 increasing
102 0.8 200.0 -199.2 decreasing
103 10.2 10.2 0.0 unchanged

sin

Use the sin() function to calculate the sine of a numeric value specified in radians.

Syntax

sin(<numeric_expression>)

Parameters

Name Type Required Description
numeric_expression integer, float Yes The angle in radians for which to calculate the sine. Any real number is valid.

Returns

Type: float

Description: The sin() function returns the sine of the input angle. The resulting value is within the range [-1, 1].

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The sin() function accepts any real number as input. The input is interpreted as an angle in radians.
  • Null Handling: If the input expression is null, the function returns null.
  • Degrees to Radians: If your input is in degrees, convert it to radians first by multiplying by PI() / 180.
  • Periodicity: The sine function is periodic with period 2π, meaning sin(x) = sin(x + 2π).
  • Key Values: sin(0) returns 0.0, sin(π/2) returns 1.0, sin(π) returns 0.0, sin(3π/2) returns -1.0.
  • Symmetry: The function is odd, meaning sin(-x) = -sin(x).
  • Common Use Cases: This function is typically used within the alter stage for trigonometric calculations, signal processing, coordinate transformations, and wave analysis.

Examples

Example 1: Calculate sine of literal values

Goal: Calculate the sine for specific radian values to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = sin(0), result2 = sin(divide(pi(), 2)), result3 = sin(pi())
| fields result1, result2, result3

Explanation: sin(0) returns 0.0, sin(π/2) returns 1.0, and sin(π) returns approximately 0.0 (a very small number close to zero due to floating-point precision).

Output:

RESULT1 RESULT2 RESULT3
0.0 1.0 0.0

Example 2: Calculate sine from a field value

Goal: Calculate the sine of values stored in a dataset field, treating them as angles in radians.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter sin_val = sin(numeric_value)
| fields event_id, numeric_value, sin_val
| limit 3

Explanation: This query computes the sine for each value in the numeric_value field and stores the result in sin_val. Since sin() accepts any real number, no filtering is needed.

Output:

EVENT_ID NUMERIC_VALUE SIN_VAL
101 0.0 0.0
102 1.5708 1.0
103 3.14159 0.0

Example 3: Use sine in a coordinate transformation

Goal: Use the sine function to compute the y-component of a polar-to-Cartesian coordinate transformation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter y_component = multiply(numeric_value, sin(duration_seconds))
| fields event_id, numeric_value, duration_seconds, y_component
| limit 3

Explanation: This query computes the y-component of a polar coordinate transformation using the formula y = r * sin(θ), where numeric_value is the radius and duration_seconds is the angle in radians.

Output:

EVENT_ID NUMERIC_VALUE DURATION_SECONDS Y_COMPONENT
101 5.0 0.0 0.0
102 3.0 1.5708 3.0
103 7.0 3.14159 0.0

split

Use the split() function to divide a string into an array of substrings based on a specified delimiter.

Syntax

split (<value> [, <string_delimiter>])

Parameters

Name Type Required Description
value string Yes The string field or literal value to split.
string_delimiter string No The string literal used as the delimiter. If omitted or set to an empty string (""), a space (' ') is used by default.

Returns

The split() function returns an array of strings containing the substrings generated by splitting the input value.

Usage notes

  • The function operates exclusively on string inputs.
  • The delimiter itself is not included in the resulting array elements.
  • If the delimiter appears consecutively, or at the beginning or end of the string, it can result in empty strings as elements in the output array.
  • If the input value is NULL, the function returns an empty array [].

Examples

Example 1: Basic split with a specific delimiter

Goal: Split an IP address string by the dot (.) character to separate its octets.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id in (101, 102, 105)
| alter ip_octets = split(ipv4_address, ".")
| fields event_id, ipv4_address, ip_octets 
| limit 3

Explanation: The function divides the ipv4_address string into an array of individual numeric octets using the specified . delimiter.

Output:

EVENT_ID IPV4_ADDRESS IP_OCTETS
101 192.168.1.10 ["192", "168", "1", "10"]
102 10.0.0.5 ["10", "0", "0", "5"]
105 192.168.10.20 ["192", "168", "10", "20"]

Example 2: Split without a specified delimiter

Goal: Split a log message into individual words using the default delimiter (space).

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id in (101, 102)
| alter log_words = split(raw_log_data)
| fields event_id, raw_log_data, log_words 
| limit 2

Explanation: By omitting the optional delimiter argument, the function automatically uses a space to break the raw_log_data string into an array of words.

Output:

EVENT_ID RAW_LOG_DATA LOG_WORDS
101 User Alice logged in from 192.168.1.10 ["User", "Alice", "logged", "in", "from", "192.168.1.10"]
102 Process cmd.exe attempted to access /etc/passwd ["Process", "cmd.exe", "attempted", "to", "access", "/etc/passwd"]

Example 3: Split with an empty string as delimiter

Goal: Explicitly use an empty string as the delimiter, which defaults to splitting by space.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 101
| alter log_parts = split(raw_log_data, "")
| fields event_id, raw_log_data, log_parts 
| limit 1

Explanation: Providing an empty string "" for the delimiter results in the same behavior as omitting the delimiter; the string is split by spaces.

Output:

EVENT_ID RAW_LOG_DATA LOG_PARTS
101 User Alice logged in from 192.168.1.10 ["User", "Alice", "logged", "in", "from", "192.168.1.10"]

Example 4: Split on a delimiter not present in the string

Goal: Attempt to split a string using a delimiter that does not exist within that string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 101
| alter no_split_example = split(event_description, "|")
| fields event_id, event_description, no_split_example 
| limit 1

Explanation: The function returns an array containing the original string as its single element because the pipe (|) character is not present in the event_description.

Output:

EVENT_ID EVENT_DESCRIPTION NO_SPLIT_EXAMPLE
101 User login successful ["User login successful"]

Example 5: Split on a field containing NULL values

Goal: Handle input fields that contain NULL values.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 105
| alter split_null_domain = split(dst_domain, ".")
| fields event_id, dst_domain, split_null_domain 
| limit 1

Explanation: When the input string field (for example, dst_domain for event ID 105) is NULL, the function returns an empty array.

Output:

EVENT_ID DST_DOMAIN SPLIT_NULL_DOMAIN
105 NULL []

Example 6: Combining split with arrayindex

Goal: Split a string and immediately extract a specific element from the resulting array.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id in (101, 102)
| alter ip_parts_array = split(ipv4_address, ".")
| alter first_octet_string = arrayindex(ip_parts_array, 0)
| fields event_id, ipv4_address, ip_parts_array, first_octet_string 
| limit 2

Explanation: The query first uses split() to tokenize the ipv4_address. Subsequently, arrayindex(..., 0) extracts the first element (the first octet) as a string.

Output:

EVENT_ID IPV4_ADDRESS IP_PARTS_ARRAY FIRST_OCTET_STRING
101 192.168.1.10 ["192", "168", "1", "10"] 192
102 10.0.0.5 ["10", "0", "0", "5"] 10

sqrt

Use the sqrt() function to calculate the square root of a numeric value.

Syntax

sqrt(<number>)

Parameters

Name Type Required Description
number integer, float Yes The numeric value for which to calculate the square root. The value must be greater than or equal to 0.

Returns

Type: float

Description: The sqrt() function returns the non-negative square root of the input value. If the input is null or negative, the function returns null or NaN.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The input number must be greater than or equal to 0. The square root of a negative number is not a real number.
  • Negative Input: If the input is negative, the function returns NaN or null.
  • Null Handling: If the input expression is null, the function returns null.
  • Identity Values: sqrt(0) returns 0.0, sqrt(1) returns 1.0.
  • Perfect Squares: For perfect squares (for example, 4, 9, 16, 25), the result is an exact integer value represented as a float.
  • Relationship to power(): sqrt(x) is equivalent to power(x, 0.5).
  • Common Use Cases: This function is typically used within the alter stage for distance calculations, standard deviation computations, normalization, and geometric formulas.

Examples

Example 1: Calculate square root of literal values

Goal: Calculate the square root for specific numeric literals to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = sqrt(4), result2 = sqrt(9), result3 = sqrt(2)
| fields result1, result2, result3

Explanation: sqrt(4) returns 2.0, sqrt(9) returns 3.0, and sqrt(2) returns approximately 1.41421.

Output:

RESULT1 RESULT2 RESULT3
2.0 3.0 1.41421

Example 2: Calculate square root from a field value

Goal: Calculate the square root of values stored in a dataset field, filtering out negative values.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter numeric_value >= 0
| alter sqrt_result = sqrt(numeric_value)
| fields event_id, numeric_value, sqrt_result
| limit 3

Explanation: This query first filters the dataset to ensure numeric_value contains only non-negative values, then computes the square root for each value using sqrt() in the alter stage.

Output:

EVENT_ID NUMERIC_VALUE SQRT_RESULT
101 4.0 2.0
102 100.0 10.0
103 25.0 5.0

Example 3: Calculate Euclidean distance using sqrt

Goal: Manually calculate the Euclidean distance between two 2D points using sqrt() and arithmetic functions.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter dx = subtract(numeric_value, 10)
| alter dy = subtract(duration_seconds, 5)
| alter distance = sqrt(add(multiply(dx, dx), multiply(dy, dy)))
| fields event_id, numeric_value, duration_seconds, distance
| limit 3

Explanation: This query calculates the Euclidean distance from each point (numeric_value, duration_seconds) to the reference point (10, 5) using the formula sqrt((x2-x1)� + (y2-y1)�).

Output:

EVENT_ID NUMERIC_VALUE DURATION_SECONDS DISTANCE
101 4.0 1.5 7.28011
102 13.0 5.0 3.0
103 10.0 10.2 5.2

stddev_population (comp)

Use the stddev_population() function to compute the population standard deviation of a specified numeric field across all rows in each group within the comp stage. Population standard deviation measures the spread of values when the data represents the entire population. This is equivalent to STDDEV_POP in SQL.

Syntax

| comp stddev_population(<field>) [by <group_field1>, <group_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
field numeric Yes The numeric field from which to compute the population standard deviation.
group_field any No One or more fields to group the results by. If omitted, all rows are treated as a single group.
alias string No An alias for the output field. If not specified, the output field name defaults to stddev_population_<field>.

Returns

Type: numeric (float)

Description: The stddev_population() function returns the population standard deviation of the specified field within each group. Returns NULL if all values in the group are NULL. Returns 0 if there is only one non-NULL value.

Usage notes

  • Population vs. sample: Use stddev_population() when the data represents the entire population. Use stddev_sample() when the data is a sample from a larger population.
  • Formula: Population standard deviation is calculated as the square root of the population variance: sqrt(sum((x - mean)^2) / N), where N is the number of non-NULL values.
  • Null handling: NULL values are ignored in the computation.
  • Single value: If there is only one non-NULL value, the population standard deviation is 0.
  • Data types: Only works with numeric fields.

Examples

Example 1: Standard deviation of response times per host

Goal: Compute the population standard deviation of response times for each host.

XQL code:

dataset = xdr_data
| comp stddev_population(action_total_time) by agent_hostname as stddev_response

Explanation: The stddev_population() function computes the population standard deviation of action_total_time for each unique agent_hostname, measuring how spread out the response times are.

Output:

AGENT_HOSTNAME STDDEV_RESPONSE
workstation-1 12.45
workstation-2 8.32

Example 2: Overall standard deviation of bytes transferred

Goal: Compute the population standard deviation of bytes transferred across all events.

XQL code:

dataset = xdr_data
| comp stddev_population(action_network_bytes_received) as bytes_stddev

Explanation: Without a by clause, the stddev_population() function computes the population standard deviation across all rows.

Output:

BYTES_STDDEV
2456.78

Example 3: Compare standard deviation with mean

Goal: Compute both the mean and standard deviation to understand data distribution per host.

XQL code:

dataset = xdr_data
| comp stddev_population(action_total_time) as stddev_time, avg(action_total_time) as avg_time by agent_hostname

Explanation: This query combines stddev_population() with avg() to provide both the average and the spread of response times per host, enabling coefficient of variation analysis.

Output:

AGENT_HOSTNAME STDDEV_TIME AVG_TIME
workstation-1 12.45 45.30
workstation-2 8.32 32.10

stddev_population (windowcomp)

Use the stddev_population() function within the windowcomp stage to compute the population standard deviation of a specified numeric field over a window of rows. Unlike the comp stage version, the windowcomp version preserves all original rows and adds the computed standard deviation as a new field. This is equivalent to STDDEV_POP() OVER(...) in SQL.

Syntax

| windowcomp stddev_population(<field>) [by <partition_field1>, <partition_field2>, ...] [sort [asc|desc] <sort_field1>, ...] [between <lower> [and <upper>] [frame_type=rows|range]] [as <alias>]

Parameters

Name Type Required Description
field numeric Yes The numeric field from which to compute the population standard deviation.
partition_field any No One or more fields to partition the data by (equivalent to SQL PARTITION BY).
sort_field any No One or more fields to define the order within each partition (equivalent to SQL ORDER BY). Defaults to ascending.
lower integer or null No Lower bound of the window frame. 0 = current row, negative = rows before, null = unbounded.
upper integer or null No Upper bound of the window frame. 0 = current row, positive = rows after, null = unbounded.
frame_type rows or range No Type of window frame. rows (default) uses physical row offsets; range uses value-based offsets on the sort field.
alias string No An alias for the output field.

Returns

Type: numeric (float)

Description: The stddev_population() function returns the population standard deviation within the defined window for each row. All original rows are preserved.

Usage notes

  • Row preservation: Unlike comp stddev_population(), the windowcomp version does not reduce the number of rows.
  • Default frame: If no window frame is specified, the default frame is from the start of the partition to the current row (between null and 0).
  • Null handling: NULL values are ignored in the computation.
  • Running standard deviation: Can be combined with sort to compute a running population standard deviation.
  • Population vs. sample: Use stddev_population() for the entire population; use stddev_sample() for a sample.

Examples

Example 1: Population standard deviation per host (all rows preserved)

Goal: Compute the population standard deviation of response times for each host while preserving all rows.

XQL code:

dataset = xdr_data
| windowcomp stddev_population(action_total_time) by agent_hostname as stddev_per_host

Explanation: The stddev_population() function computes the population standard deviation of action_total_time within each agent_hostname partition. All original rows are preserved.

Output:

_TIME AGENT_HOSTNAME ACTION_TOTAL_TIME STDDEV_PER_HOST
2024-01-15 08:00:00 workstation-1 50 16.33
2024-01-15 09:00:00 workstation-1 30 16.33
2024-01-15 10:00:00 workstation-1 70 16.33
2024-01-15 08:30:00 workstation-2 40 5.00
2024-01-15 09:30:00 workstation-2 50 5.00

Example 2: Running standard deviation over time

Goal: Compute a running population standard deviation of bytes received, ordered by time.

XQL code:

dataset = xdr_data
| windowcomp stddev_population(action_network_bytes_received) sort asc _time as running_stddev

Explanation: By sorting on _time, the function computes a running population standard deviation from the start of the dataset up to the current row.

Output:

_TIME ACTION_NETWORK_BYTES_RECEIVED RUNNING_STDDEV
2024-01-15 08:00:00 500 0.00
2024-01-15 09:00:00 250 125.00
2024-01-15 10:00:00 400 102.14
2024-01-15 11:00:00 100 147.90

Example 3: Standard deviation within a sliding window

Goal: Compute the population standard deviation within a sliding window of 5 rows.

XQL code:

dataset = xdr_data
| windowcomp stddev_population(action_total_time) sort asc _time between -2 and 2 as sliding_stddev

Explanation: The between -2 and 2 clause defines a sliding window of up to 5 rows centered on the current row. The function computes the population standard deviation within that window.

Output:

_TIME ACTION_TOTAL_TIME SLIDING_STDDEV
2024-01-15 08:00:00 50 12.47
2024-01-15 09:00:00 30 14.79
2024-01-15 10:00:00 70 16.33
2024-01-15 11:00:00 40 14.14

stddev_sample (comp)

Use the stddev_sample() function to compute the sample standard deviation of a specified numeric field across all rows in each group within the comp stage. Sample standard deviation uses Bessel's correction (dividing by N-1 instead of N) and is appropriate when the data represents a sample from a larger population. This is equivalent to STDDEV_SAMP in SQL.

Syntax

| comp stddev_sample(<field>) [by <group_field1>, <group_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
field numeric Yes The numeric field from which to compute the sample standard deviation.
group_field any No One or more fields to group the results by. If omitted, all rows are treated as a single group.
alias string No An alias for the output field. If not specified, the output field name defaults to stddev_sample_<field>.

Returns

Type: numeric (float)

Description: The stddev_sample() function returns the sample standard deviation of the specified field within each group. Returns NULL if there are fewer than two non-NULL values in the group.

Usage notes

  • Sample vs. population: Use stddev_sample() when the data is a sample from a larger population. Use stddev_population() when the data represents the entire population.
  • Formula: Sample standard deviation is calculated as: sqrt(sum((x - mean)^2) / (N - 1)), where N is the number of non-NULL values (Bessel's correction).
  • Minimum values: Requires at least two non-NULL values to produce a result. Returns NULL for groups with fewer than two values.
  • Null handling: NULL values are ignored in the computation.
  • Data types: Only works with numeric fields.

Examples

Example 1: Sample standard deviation of response times per host

Goal: Compute the sample standard deviation of response times for each host.

XQL code:

dataset = xdr_data
| comp stddev_sample(action_total_time) by agent_hostname as sample_stddev

Explanation: The stddev_sample() function computes the sample standard deviation of action_total_time for each unique agent_hostname, using Bessel's correction for unbiased estimation.

Output:

AGENT_HOSTNAME SAMPLE_STDDEV
workstation-1 14.42
workstation-2 9.62

Example 2: Overall sample standard deviation

Goal: Compute the sample standard deviation of bytes transferred across all events.

XQL code:

dataset = xdr_data
| comp stddev_sample(action_network_bytes_received) as bytes_sample_stddev

Explanation: Without a by clause, the stddev_sample() function computes the sample standard deviation across all rows.

Output:

BYTES_SAMPLE_STDDEV
2678.91

Example 3: Compare population and sample standard deviations

Goal: Compare population and sample standard deviations to understand the effect of Bessel's correction.

XQL code:

dataset = xdr_data
| comp stddev_sample(action_total_time) as sample_std, stddev_population(action_total_time) as pop_std, count(action_total_time) as n by agent_hostname

Explanation: This query computes both stddev_sample() and stddev_population() alongside the count, showing how the sample standard deviation is slightly larger due to Bessel's correction (dividing by N-1 instead of N).

Output:

AGENT_HOSTNAME SAMPLE_STD POP_STD N
workstation-1 14.42 12.45 5
workstation-2 9.62 8.32 4

stddev_sample (windowcomp)

Use the stddev_sample() function within the windowcomp stage to compute the sample standard deviation of a specified numeric field over a window of rows. Unlike the comp stage version, the windowcomp version preserves all original rows and adds the computed sample standard deviation as a new field. This is equivalent to STDDEV_SAMP() OVER(...) in SQL.

Syntax

| windowcomp stddev_sample(<field>) [by <partition_field1>, <partition_field2>, ...] [sort [asc|desc] <sort_field1>, ...] [between <lower> [and <upper>] [frame_type=rows|range]] [as <alias>]

Parameters

Name Type Required Description
field numeric Yes The numeric field from which to compute the sample standard deviation.
partition_field any No One or more fields to partition the data by (equivalent to SQL PARTITION BY).
sort_field any No One or more fields to define the order within each partition (equivalent to SQL ORDER BY). Defaults to ascending.
lower integer or null No Lower bound of the window frame. 0 = current row, negative = rows before, null = unbounded.
upper integer or null No Upper bound of the window frame. 0 = current row, positive = rows after, null = unbounded.
frame_type rows or range No Type of window frame. rows (default) uses physical row offsets; range uses value-based offsets on the sort field.
alias string No An alias for the output field.

Returns

Type: numeric (float)

Description: The stddev_sample() function returns the sample standard deviation (using Bessel's correction, N-1) within the defined window for each row. All original rows are preserved. Returns NULL when the window contains fewer than two non-NULL values.

Usage notes

  • Row preservation: Unlike comp stddev_sample(), the windowcomp version does not reduce the number of rows.
  • Bessel's correction: Uses N-1 in the denominator for unbiased estimation from a sample.
  • Default frame: If no window frame is specified, the default frame is from the start of the partition to the current row (between null and 0).
  • Null handling: NULL values are ignored in the computation.
  • Minimum values: Requires at least two non-NULL values in the window to produce a result.
  • Running standard deviation: Can be combined with sort to compute a running sample standard deviation.

Examples

Example 1: Sample standard deviation per host (all rows preserved)

Goal: Compute the sample standard deviation of response times for each host while preserving all rows.

XQL code:

dataset = xdr_data
| windowcomp stddev_sample(action_total_time) by agent_hostname as sample_stddev_per_host

Explanation: The stddev_sample() function computes the sample standard deviation of action_total_time within each agent_hostname partition using Bessel's correction. All original rows are preserved.

Output:

_TIME AGENT_HOSTNAME ACTION_TOTAL_TIME SAMPLE_STDDEV_PER_HOST
2024-01-15 08:00:00 workstation-1 50 20.00
2024-01-15 09:00:00 workstation-1 30 20.00
2024-01-15 10:00:00 workstation-1 70 20.00
2024-01-15 08:30:00 workstation-2 40 7.07
2024-01-15 09:30:00 workstation-2 50 7.07

Example 2: Running sample standard deviation over time

Goal: Compute a running sample standard deviation of bytes received, ordered by time.

XQL code:

dataset = xdr_data
| windowcomp stddev_sample(action_network_bytes_received) sort asc _time as running_sample_stddev

Explanation: By sorting on _time, the function computes a running sample standard deviation from the start of the dataset up to the current row. The first row returns NULL because at least two values are needed.

Output:

_TIME ACTION_NETWORK_BYTES_RECEIVED RUNNING_SAMPLE_STDDEV
2024-01-15 08:00:00 500 null
2024-01-15 09:00:00 250 176.78
2024-01-15 10:00:00 400 125.83
2024-01-15 11:00:00 100 170.78

Example 3: Sample standard deviation within a sliding window

Goal: Compute the sample standard deviation within a sliding window of 3 rows.

XQL code:

dataset = xdr_data
| windowcomp stddev_sample(action_total_time) sort asc _time between -1 and 1 as sliding_sample_stddev

Explanation: The between -1 and 1 clause defines a sliding window of up to 3 rows centered on the current row. The function computes the sample standard deviation within that window using Bessel's correction.

Output:

_TIME ACTION_TOTAL_TIME SLIDING_SAMPLE_STDDEV
2024-01-15 08:00:00 50 14.14
2024-01-15 09:00:00 30 20.00
2024-01-15 10:00:00 70 20.82
2024-01-15 11:00:00 40 21.21

string_count

Use the string_count() function to count the number of times a specified substring (pattern) appears within a given string.

Syntax

string_count (<string>, <pattern>)

Parameters

Name Type Required Description
string string Yes The input string field or literal value in which you want to search.
pattern string Yes The substring literal that you want to count.

Returns

The string_count() function returns an integer representing the number of occurrences found.

Usage notes

  • By default, XQL queries operate with case-sensitivity unless config case_sensitive = false is explicitly set. This also applies to string_count().
  • If the input string value is NULL, the string_count() function will return NULL.
  • If the pattern does not appear in the string, the function returns 0.

Examples

Example 1: Counting occurrences of a specific character in a string field

Goal: Count the occurrences of the character 'o' in the event_description field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id in (101, 102, 103) // Focus on relevant records from sample_xql_raw
| alter count_of_o = string_count(event_description, "o") // Counts occurrences of 'o' 
| fields event_id, event_description, count_of_o 

Explanation: The query counts how many times the character 'o' appears in the event_description for each specified event.

Output:

EVENT_ID EVENT_DESCRIPTION COUNT_OF_O
101 "User login successful" 1
102 "File access attempt" 0
103 "Network connection established" 3

Example 2: Counting occurrences of a substring (word) in a string field

Goal: Count the occurrences of the word "access" in the raw_log_data field.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id in (101, 102) // Focus on relevant records
| alter count_of_access = string_count(raw_log_data, "access") // Counts occurrences of 'access' 
| fields event_id, raw_log_data, count_of_access 

Explanation: The string_count() function correctly identifies and counts the single occurrence of "access" in event ID 102's raw_log_data.

Output:

EVENT_ID RAW_LOG_DATA COUNT_OF_ACCESS
101 "User Alice logged in from 192.168.1.10" 0
102 "Process cmd.exe attempted to access /etc/passwd" 1

Example 3: Counting delimiters in an IP address string

Goal: Count the number of dot (.) delimiters within ipv4_address strings.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id in (101, 102) // Focus on relevant records
| alter count_of_dots = string_count(ipv4_address, ".") // Counts occurrences of '.' 
| fields event_id, ipv4_address, count_of_dots 

Explanation: The query accurately counts the three dot separators in each IPv4 address string.

Output:

EVENT_ID IPV4_ADDRESS COUNT_OF_DOTS
101 "192.168.1.10" 3
102 "10.0.0.5" 3

Example 4: Case-insensitive counting (default behavior)

Goal: Demonstrate string_count()'s default case-sensitive behavior by searching for "user" (lowercase) in a field containing "User" (uppercase).

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 101 // Focus on a single record
| alter count_user_lowercase_search = string_count(raw_log_data, "user") // Searches for 'user' 
| fields event_id, raw_log_data, count_user_lowercase_search 

Explanation: The pattern "user" (lowercase) does not successfully match "User" in the raw_log_data due to the case-sensitive behavior of XQL.

Output:

EVENT_ID RAW_LOG_DATA COUNT_USER_LOWERCASE_SEARCH
101 "User Alice logged in from 192.168.1.10" 0

Example 5: Case-sensitive counting (with config case_sensitive = false)

Goal: Explicitly set case sensitivity to false to show how it affects string_count() results when searching for "user" vs. "User".

XQL code:

config case_sensitive = false // Explicitly enable case in-sensitivity 
| dataset = sample_xql_raw
| filter event_id = 101 // Focus on a single record
| alter count_User_uppercase_search = string_count(raw_log_data, "User") // Searches for 'User' 
| alter count_user_lowercase_search = string_count(raw_log_data, "user") // Searches for 'user' 
| fields event_id, raw_log_data, count_User_uppercase_search, count_user_lowercase_search 

Explanation: When config case_sensitive is false, string_count() does not distinguish between "User" and "user", returning 1 for both cases.

Output:

EVENT_ID RAW_LOG_DATA COUNT_USER_UPPERCASE_SEARCH COUNT_USER_LOWERCASE_SEARCH
101 "User Alice logged in from 192.168.1.10" 1 1

Example 6: Counting a pattern not present in the string

Goal: Demonstrate the result when the specified pattern does not exist in the input string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 101 // Focus on a single record
| alter count_of_nonexistent_pattern = string_count(event_description, "nonexistent_word") // Searches for a non-existent word 
| fields event_id, event_description, count_of_nonexistent_pattern 

Explanation: As expected, string_count() returns 0 when the nonexistent_word pattern is not found in the event_description.

Output:

EVENT_ID EVENT_DESCRIPTION COUNT_OF_NONEXISTENT_PATTERN
101 "User login successful" 0

Example 7: Handling NULL input string

Goal: Show how string_count() behaves when the input string field contains a NULL value.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter count_in_null_domain = string_count(dst_domain, ".") // Attempts to count in a NULL domain 
| fields event_id, dst_domain, count_in_null_domain 
| filter event_id = 105 // Focus on record with NULL dst_domain 

Explanation: Consistent with XQL function behavior, if the input string (dst_domain for event ID 105) is NULL, the string_count() function returns NULL.

Output:

EVENT_ID DST_DOMAIN COUNT_IN_NULL_DOMAIN
105 NULL NULL

subtract

Use the subtract() function to calculate the difference between two numbers by subtracting the second argument from the first.

Syntax

subtract (<value_1>, <value_2>)

Parameters

Name Type Required Description
value_1 integer, float, string Yes The first value (the minuend).
value_2 integer, float, string Yes The second value (the subtrahend) to subtract from the first.

Returns

The subtract() function returns the numerical difference between the two input numbers.

Usage notes

  • The function accepts numeric literals, floating-point numbers, and integers.
  • The function supports integers or numbers provided as a string type (for example, extracted from a data field).

Examples

Example 1: Subtracting an integer literal from an integer field

Goal: Subtract a specific integer literal from an existing integer field to create a new field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter reduced_event_id = subtract(event_id, 5)
| fields event_id, reduced_event_id
| limit 3

Explanation: This query calculates reduced_event_id by subtracting 5 from each event_id. For event_id 101, the result is 96.

Output:

EVENT_ID REDUCED_EVENT_ID
101 96
102 97
103 98

Example 2: Subtracting a floating-point literal from a floating-point field

Goal: Subtract a floating-point literal from an existing floating-point field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter adjusted_duration = subtract(duration_seconds, 0.75)
| fields event_id, duration_seconds, adjusted_duration
| limit 3

Explanation: Here, duration_seconds (which contains decimal values like 1.5 and 0.8) is decreased by 0.75, producing new floating-point values in adjusted_duration.

Output:

EVENT_ID DURATION_SECONDS ADJUSTED_DURATION
101 1.5 0.75
102 0.8 0.05
103 10.2 9.45

Example 3: Subtracting one field from another field

Goal: Calculate the difference between values in two different fields.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter difference_in_ids = subtract(event_id, arrayindex(numeric_codes, 0))
| fields event_id, numeric_codes, difference_in_ids
| limit 3

Explanation: For event_id 101, event_id is 101 and the first element of numeric_codes (index 0) is 13, resulting in 101 - 13 = 88. For event_id 102, event_id is 102 and the first numeric_code is -21, so 102 - (-21) = 123.

Output:

EVENT_ID NUMERIC_CODES DIFFERENCE_IN_IDS
101 [13, -47, 29,...] 88
102 [-21, 56, 13,...] 123
103 [90, -33, 7,...] 13

Example 4: Handling negative numbers (subtracting a negative literal)

Goal: Demonstrate the behavior when subtracting a negative number.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter negative_subtraction = subtract(duration_seconds, -2.0)
| fields event_id, duration_seconds, negative_subtraction
| limit 3

Explanation: For event_id 101, duration_seconds is 1.5. Subtracting -2.0 (which is equivalent to adding 2.0) results in 3.5. This aligns with the understanding that the subtract() function supports operations with negative numbers.

Output:

EVENT_ID DURATION_SECONDS NEGATIVE_SUBTRACTION
101 1.5 3.5
102 0.8 2.8
103 10.2 12.2

Example 5: Subtracting a numeric value extracted from JSON data

Goal: Extract a number from a JSON field and perform a subtraction operation on it.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter simple_json_data != null
| alter json_code_number = to_number(simple_json_data -> "code")
| alter reduced_json_value = subtract(json_code_number, 50)
| fields event_id, simple_json_data, json_code_number, reduced_json_value
| limit 3

Explanation: This query extracts the code value (for example, "200") from the simple_json_data field as a string, converts it to a number using to_number(), and then subtracts 50 from it. Note that only event_id 101 has a "code" field in simple_json_data in the sample_xql_raw dataset.

Output:

EVENT_ID SIMPLE_JSON_DATA JSON_CODE_NUMBER REDUCED_JSON_VALUE
101 {"status": "ok", "code": 200} 200 150

sum (comp)

Use the sum() function to compute the sum of all values of a specified numeric field across all rows in each group within the comp stage. This is equivalent to the SQL SUM aggregate function.

Syntax

| comp sum(<field>) [by <group_field1>, <group_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
field numeric Yes The numeric field whose values will be summed.
group_field any No One or more fields to group the results by. If omitted, all rows are treated as a single group.
alias string No An alias for the output field. If not specified, the output field name defaults to sum_<field>.

Returns

Type: numeric

Description: The sum() function returns the total sum of all non-NULL values of the specified field within each group. Returns NULL if all values in the group are NULL. Returns 0 if the group is empty.

Usage notes

  • Data types: The sum function only works with numeric fields.
  • Null handling: NULL values are ignored in the computation.
  • No grouping: When used without a by clause, the function returns the sum across all rows.
  • Multiple aggregations: Can be combined with other aggregation functions in the same comp stage.
  • Overflow: For very large datasets with large values, be aware of potential numeric overflow.

Examples

Example 1: Total bytes sent per host

Goal: Compute the total bytes sent for each host.

XQL code:

dataset = xdr_data
| comp sum(bytes_sent) by agent_hostname as total_bytes_sent

Explanation: The sum() function adds up all bytes_sent values for each unique agent_hostname, returning the total bytes sent per host.

Output:

AGENT_HOSTNAME TOTAL_BYTES_SENT
workstation-1 1048576
workstation-2 524288

Example 2: Total events across all data

Goal: Compute the total sum of alert severities across all events.

XQL code:

dataset = xdr_data
| comp sum(alert_severity) as total_severity

Explanation: Without a by clause, the sum() function computes the total of all alert_severity values across the entire dataset.

Output:

TOTAL_SEVERITY
342

Example 3: Sum with multiple aggregations

Goal: Compute total bytes sent and received along with event count per host.

XQL code:

dataset = xdr_data
| comp sum(bytes_sent) as total_sent, sum(action_network_bytes_received) as total_received, count(*) as event_count by agent_hostname

Explanation: This query combines multiple sum() calls with count() in a single comp stage to provide a comprehensive traffic summary per host.

Output:

AGENT_HOSTNAME TOTAL_SENT TOTAL_RECEIVED EVENT_COUNT
workstation-1 1048576 2097152 150
workstation-2 524288 1048576 85

sum (windowcomp)

Use the sum() function within the windowcomp stage to compute the sum of a specified numeric field over a window of rows. Unlike the comp stage version, the windowcomp version preserves all original rows and adds the computed sum as a new field. This is equivalent to SUM() OVER(...) in SQL.

Syntax

| windowcomp sum(<field>) [by <partition_field1>, <partition_field2>, ...] [sort [asc|desc] <sort_field1>, ...] [between <lower> [and <upper>] [frame_type=rows|range]] [as <alias>]

Parameters

Name Type Required Description
field numeric Yes The numeric field whose values will be summed over the window.
partition_field any No One or more fields to partition the data by (equivalent to SQL PARTITION BY).
sort_field any No One or more fields to define the order within each partition (equivalent to SQL ORDER BY). Defaults to ascending.
lower integer or null No Lower bound of the window frame. 0 = current row, negative = rows before, null = unbounded.
upper integer or null No Upper bound of the window frame. 0 = current row, positive = rows after, null = unbounded.
frame_type rows or range No Type of window frame. rows (default) uses physical row offsets; range uses value-based offsets on the sort field.
alias string No An alias for the output field.

Returns

Type: numeric

Description: The sum() function returns the sum of all non-NULL values within the defined window for each row. All original rows are preserved.

Usage notes

  • Row preservation: Unlike comp sum(), the windowcomp sum() does not reduce the number of rows. Each row retains its original data and gets an additional field with the window sum.
  • Running total: When combined with sort, the default frame (between null and 0) produces a cumulative running total.
  • No partitioning: If no by clause is specified, the window spans the entire result set.
  • Default frame: If no window frame is specified, the default frame is from the start of the partition to the current row (between null and 0).
  • Null handling: NULL values are ignored in the computation.

Examples

Example 1: Cumulative bytes sent over time

Goal: Compute a running total of bytes sent, ordered by time.

XQL code:

dataset = xdr_data
| windowcomp sum(bytes_sent) sort asc _time as cumulative_bytes

Explanation: By sorting on _time in ascending order, the sum() function computes a cumulative running total of bytes_sent from the start of the dataset up to the current row.

Output:

_TIME BYTES_SENT CUMULATIVE_BYTES
2024-01-15 08:00:00 100 100
2024-01-15 09:00:00 250 350
2024-01-15 10:00:00 150 500
2024-01-15 11:00:00 300 800

Example 2: Total bytes per host (all rows preserved)

Goal: Compute the total bytes sent per host while preserving all original rows.

XQL code:

dataset = xdr_data
| windowcomp sum(bytes_sent) by agent_hostname as total_bytes_per_host

Explanation: The sum() function computes the total bytes_sent within each agent_hostname partition. All original rows are preserved, and each row receives the partition's total.

Output:

_TIME AGENT_HOSTNAME BYTES_SENT TOTAL_BYTES_PER_HOST
2024-01-15 08:00:00 workstation-1 100 500
2024-01-15 09:00:00 workstation-1 250 500
2024-01-15 10:00:00 workstation-1 150 500
2024-01-15 08:30:00 workstation-2 300 700
2024-01-15 09:30:00 workstation-2 400 700

Example 3: Moving sum within a sliding window

Goal: Compute the sum of bytes sent within a sliding window of 3 rows.

XQL code:

dataset = xdr_data
| windowcomp sum(bytes_sent) sort asc _time between -1 and 1 as moving_sum

Explanation: The between -1 and 1 clause defines a sliding window of 3 rows centered on the current row. The sum() function returns the total bytes_sent within that window for each row.

Output:

_TIME BYTES_SENT MOVING_SUM
2024-01-15 08:00:00 100 350
2024-01-15 09:00:00 250 500
2024-01-15 10:00:00 150 700
2024-01-15 11:00:00 300 450

tan

Use the tan() function to calculate the tangent of a numeric value specified in radians.

Syntax

tan(<numeric_expression>)

Parameters

Name Type Required Description
numeric_expression integer, float Yes The angle in radians for which to calculate the tangent. Any real number is valid, though the tangent is undefined at odd multiples of p/2.

Returns

Type: float

Description: The tan() function returns the tangent of the input angle. The result can be any real number. If the input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Valid Input Range: The tan() function accepts any real number as input. The input is interpreted as an angle in radians.
  • Undefined Values: At odd multiples of p/2 (for example, p/2, 3p/2), the tangent is mathematically undefined and the function may return a very large number, infinity, or NaN.
  • Null Handling: If the input expression is null, the function returns null.
  • Degrees to Radians: If your input is in degrees, convert it to radians first by multiplying by PI() / 180.
  • Periodicity: The tangent function is periodic with period p, meaning tan(x) = tan(x + p).
  • Key Values: tan(0) returns 0.0, tan(p/4) returns 1.0, tan(p) returns 0.0.
  • Symmetry: The function is odd, meaning tan(-x) = -tan(x).
  • Common Use Cases: This function is typically used within the alter stage for trigonometric calculations, slope computations, angle analysis, and engineering formulas.

Examples

Example 1: Calculate tangent of literal values

Goal: Calculate the tangent for specific radian values to verify known mathematical results.

XQL code:

dataset = xdr_data
| limit 1
| alter result1 = tan(0), result2 = tan(divide(pi(), 4)), result3 = tan(pi())
| fields result1, result2, result3

Explanation: tan(0) returns 0.0, tan(p/4) returns 1.0 (since sine and cosine are equal at p/4), and tan(p) returns approximately 0.0.

Output:

RESULT1 RESULT2 RESULT3
0.0 1.0 0.0

Example 2: Calculate tangent from a field value

Goal: Calculate the tangent of values stored in a dataset field, treating them as angles in radians.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter tan_val = tan(numeric_value)
| fields event_id, numeric_value, tan_val
| limit 3

Explanation: This query computes the tangent for each value in the numeric_value field and stores the result in tan_val. Since tan() accepts any real number, no filtering is needed.

Output:

EVENT_ID NUMERIC_VALUE TAN_VAL
101 0.0 0.0
102 0.7854 1.0
103 1.0 1.55741

Example 3: Calculate slope angle from rise and run

Goal: Use the tangent function to verify a slope calculation by computing tan(atan(rise/run)).

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter duration_seconds > 0
| alter slope = divide(numeric_value, duration_seconds)
| alter angle = atan(slope)
| alter tan_angle = tan(angle)
| fields event_id, numeric_value, duration_seconds, slope, tan_angle
| limit 3

Explanation: This query calculates the slope as numeric_value / duration_seconds, then computes the angle using atan(), and finally verifies by computing tan() of that angle. The tan_angle should match the original slope, confirming the identity tan(atan(x)) = x.

Output:

EVENT_ID NUMERIC_VALUE DURATION_SECONDS SLOPE TAN_ANGLE
101 3.0 4.0 0.75 0.75
102 5.0 1.0 5.0 5.0
103 10.0 10.0 1.0 1.0

time_frame_end

Use the time_frame_end() function to return the timestamp object that represents the end of the time frame configured for the overall XQL query.

Syntax

time_frame_end()

Parameters

Name Type Required Description
None N/A No This function does not require any input parameters. The function inherits the time frame from the config timeframe stage.

Returns

The time_frame_end() function returns a TIMESTAMP object representing the end of the query's time range.

Usage notes

  • The function returns the timestamp object in the format MMM dd YYYY HH:mm:ss (for example, Jun 8th 2022 15:20:06).
  • The value returned directly corresponds to the end of the time range set by the config timeframe stage of the query.
  • You can configure the time frame using the config timeframe stage, where the range can be relative or exact.
  • If the config timeframe is set to a relative time (for example, last 24H or between "-1h" and "now"), time_frame_end() returns the current_time() at the moment the query is executed.
  • This function is useful when the query uses a custom time frame whose end time is in the past.

Examples

Example 1: With a relative timeframe (last N units)

Goal: Capture the end time of the query when using a relative timeframe (for example, last 2 hours).

XQL code:

config timeframe = 2h // Sets the query timeframe to the last 2 hours from execution
| dataset = sample_xql_raw
| alter query_end_time = time_frame_end() // Captures the end of the query timeframe
| fields event_id, query_end_time
| limit 3

Explanation: The config timeframe = 2h specifies that the query should run for the last two hours from the moment it is executed, and because this is a relative timeframe, time_frame_end() returns the current_time at the moment of query execution.

Output:

EVENT_ID QUERY_END_TIME
101 Oct 26th 2023 12:00:00
102 Oct 26th 2023 12:00:00
103 Oct 26th 2023 12:00:00

Example 2: With a relative timeframe (between relative start and "now")

Goal: Capture the end time of the query when using a relative timeframe ending in "now".

XQL code:

config timeframe between "-1h" and "now" // Sets the timeframe from 1 hour ago until now
| dataset = sample_xql_raw
| alter query_end_time = time_frame_end() // Captures the end of the query timeframe
| fields event_id, query_end_time
| limit 3

Explanation: Similar to Example 1, the use of "now" in the config timeframe means that time_frame_end() returns the current_time at the point of query execution. The resulting query_end_time reflects this execution time.

Output:

EVENT_ID QUERY_END_TIME
101 Oct 26th 2023 12:00:00
102 Oct 26th 2023 12:00:00
103 Oct 26th 2023 12:00:00

Example 3: With an exact timeframe

Goal: Capture the end time of the query when using a precise, static start and end time.

XQL code:

config timeframe between "2023-10-26 09:00:00 UTC" and "2023-10-26 11:00:00 UTC" // Defines an exact timeframe
| dataset = sample_xql_raw
| alter query_end_time = time_frame_end() // Captures the end of the query's timeframe
| fields event_id, query_end_time
| limit 3

Explanation: When an exact timeframe is configured, time_frame_end() directly returns the explicitly defined end timestamp from the config timeframe. In this case, it returns the timestamp for 11:00:00 UTC on October 26th, 2023.

Output:

EVENT_ID QUERY_END_TIME
101 Oct 26th 2023 11:00:00
102 Oct 26th 2023 11:00:00
103 Oct 26th 2023 11:00:00

timestamp_diff

Use the timestamp_diff() function to calculate the numerical difference between two timestamps in a specified unit.

Syntax

timestamp_diff (<timestamp1>, <timestamp2>, <part>)

Parameters

Name Type Required Description
timestamp1 timestamp Yes The first timestamp object in the comparison.
timestamp2 timestamp Yes The second timestamp object, which is subtracted from the first.
part string Yes The unit in which the difference is expressed. Supported values are DAY, HOUR, MINUTE, SECOND, MILLISECOND, and MICROSECOND.

Returns

The timestamp_diff() function returns a numerical value (integer or float) representing the difference between the two timestamps in the specified unit.

Usage notes

  • The function calculates the difference by subtracting timestamp2 from timestamp1.
  • If timestamp1 is chronologically greater (later) than timestamp2, the function returns a positive value.
  • If timestamp1 is chronologically less (earlier) than timestamp2, the function returns a negative value.
  • If the calculated difference in the specified unit results in a fractional value between 0 and 1 (exclusive of 1, inclusive of 0), the function returns 0.
  • Supported values for the part parameter include: DAY, HOUR, MINUTE, SECOND, MILLISECOND, and MICROSECOND.

Examples

Example 1: Difference in seconds between current_time() and_time

Goal: Calculate the difference in seconds between the current query execution time and the event's timestamp.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter current_ts = current_time()
| alter event_ts = _time
| alter diff_in_seconds = timestamp_diff(current_ts, event_ts, "SECOND")
| fields event_id, event_ts, current_ts, diff_in_seconds
| limit 3

Explanation: The query computes the number of full seconds that have passed between the event's _time and the current_time() of the query execution.

Output:

EVENT_ID EVENT_TS CURRENT_TS DIFF_IN_SECONDS
101 Oct 26th 2023 10:00:00 Oct 26th 2023 12:00:00 7200
102 Oct 26th 2023 10:05:30 Oct 26th 2023 12:00:00 6870
103 Oct 26th 2023 10:15:15 Oct 26th 2023 12:00:00 6285

Example 2: Difference in minutes between _time and a static timestamp

Goal: Calculate the difference in minutes between the event timestamp and a specific static timestamp, demonstrating truncation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_ts = _time
| alter static_past_ts = parse_timestamp("%Y-%m-%d %H:%M:%S", "2023-10-26 10:05:00")
| alter diff_in_minutes = timestamp_diff(event_ts, static_past_ts, "MINUTE")
| fields event_id, event_ts, static_past_ts, diff_in_minutes
| limit 3

Explanation: The query calculates the difference in minutes. Note that partial minutes (values between 0 and 1) are truncated to 0.

Output:

EVENT_ID EVENT_TS STATIC_PAST_TS DIFF_IN_MINUTES
101 Oct 26th 2023 10:00:00 Oct 26th 2023 10:05:00 -5
102 Oct 26th 2023 10:05:30 Oct 26th 2023 10:05:00 0
103 Oct 26th 2023 10:15:15 Oct 26th 2023 10:05:00 10

Example 3: Difference in hours, yielding a negative result

Goal: Calculate the difference in hours where the first timestamp is earlier than the second, resulting in a negative value.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_ts = _time
| alter static_future_ts = parse_timestamp("%Y-%m-%d %H:%M:%S", "2023-10-27 10:00:00")
| alter diff_in_hours = timestamp_diff(event_ts, static_future_ts, "HOUR")
| fields event_id, event_ts, static_future_ts, diff_in_hours
| limit 3

Explanation: The query demonstrates that when timestamp1 is earlier than timestamp2, the result is negative. Calculations are truncated to full hours.

Output:

EVENT_ID EVENT_TS STATIC_FUTURE_TS DIFF_IN_HOURS
101 Oct 26th 2023 10:00:00 Oct 27th 2023 10:00:00 -24
102 Oct 26th 2023 10:05:30 Oct 27th 2023 10:00:00 -23
103 Oct 26th 2023 10:15:15 Oct 27th 2023 10:00:00 -23

Example 4: Using timestamp_diff() in a filter stage

Goal: Filter events based on a calculated time difference relative to the current time.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter current_ts = current_time()
| filter timestamp_diff(current_ts, _time, "HOUR") > 1
| fields event_id, _time
| limit 3

Explanation: This query filters for events that occurred more than 1 hour prior to the current execution time.

Output:

EVENT_ID _TIME
101 Oct 26th 2023 10:00:00
102 Oct 26th 2023 10:05:30
103 Oct 26th 2023 10:15:15

Example 5: Difference in milliseconds between _time and a closely past static timestamp

Goal: Calculate a fine-grained difference in milliseconds between two close timestamps.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_ts = _time
| alter static_slightly_past_ts = parse_timestamp("%F %R:%E3S", "2023-10-26 10:00:00.000")
| alter diff_in_milliseconds = timestamp_diff(event_ts, static_slightly_past_ts, "MILLISECOND")
| fields event_id, event_ts, static_slightly_past_ts, diff_in_milliseconds
| limit 3

Explanation: The query calculates the exact difference in milliseconds, useful for precise time measurements.

Output:

EVENT_ID EVENT_TS STATIC_SLIGHTLY_PAST_TS DIFF_IN_MILLISECONDS
101 Oct 26th 2023 10:00:00 Oct 26th 2023 10:00:00 0
102 Oct 26th 2023 10:05:30 Oct 26th 2023 10:00:00 330000
103 Oct 26th 2023 10:15:15 Oct 26th 2023 10:00:00 915000

timestamp_seconds

Use the timestamp_seconds() function to convert an integer value representing Unix epoch time in seconds into a timestamp.

Syntax

timestamp_seconds (<integer>)

Parameters

Name Type Required Description
integer integer Yes The numerical value representing epoch time in seconds. This can be a literal integer or a field containing an integer.

Returns

The timestamp_seconds() function returns a timestamp compatible value, typically formatted as MMM dd YYYY HH:mm:ss.

Usage notes

  • The function expects a single integer input, which is implicitly interpreted as the number of seconds that have passed since the Unix epoch (January 1, 1970, 00:00:00 UTC).
  • Endpoint Detection and Response (EDR) columns often store epoch values in milliseconds. Ensure your input is in seconds; otherwise, consider using to_timestamp() with the appropriate unit specification.
  • If the input integer field or literal is NULL, the function returns NULL.
  • If the integer input value is not a sensible epoch time (for example, an extremely large or small non-epoch value), the function may return NULL or an unexpected timestamp.

Examples

Example 1: Converting a literal epoch second integer

Goal: Convert a static integer literal (representing a known epoch second value) into a human-readable timestamp.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter known_epoch_seconds = 1698304800
| alter converted_timestamp = timestamp_seconds(known_epoch_seconds)
| fields event_id, known_epoch_seconds, converted_timestamp
| limit 3

Explanation: The query takes the known_epoch_seconds integer (which is 1698304800, representing 2023-10-26 10:00:00 UTC) and converts it to its corresponding timestamp value.

Output:

EVENT_ID KNOWN_EPOCH_SECONDS CONVERTED_TIMESTAMP
101 1698304800 Oct 26th 2023 10:00:00
102 1698304800 Oct 26th 2023 10:00:00
103 1698304800 Oct 26th 2023 10:00:00

Example 2: Converting an integer field to a timestamp

Goal: Apply the function to an existing integer field to interpret it as an epoch timestamp.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_id_as_timestamp = timestamp_seconds(to_integer(event_id))
| fields event_id, event_id_as_timestamp
| limit 3

Explanation: The integer value of event_id is interpreted as the number of seconds past the Unix epoch. For example, 101 becomes 101 seconds past epoch.

Output:

EVENT_ID EVENT_ID_AS_TIMESTAMP
101 Jan 1st 1970 00:01:41
102 Jan 1st 1970 00:01:42
103 Jan 1st 1970 00:01:43

Example 3: Converting a derived integer from a JSON field to a timestamp

Goal: Extract a numerical value from a JSON field, convert it to an integer, and then convert that integer to a timestamp.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter status_code_string = simple_json_data -> code
| alter status_code_int = to_integer(status_code_string)
| alter status_code_timestamp = timestamp_seconds(status_code_int)
| fields event_id, simple_json_data, status_code_string, status_code_int, status_code_timestamp
| limit 3

Explanation: For event 101, the code value 200 is extracted, converted to an integer, and interpreted as 200 seconds from epoch. For events where the field is missing, the result is NULL.

Output:

EVENT_ID SIMPLE_JSON_DATA STATUS_CODE_STRING STATUS_CODE_INT STATUS_CODE_TIMESTAMP
101 {"status": "ok", "code": 200} "200" 200 Jan 1st 1970 00:03:20
102 {"status": "fail", "error": "access_denied"} NULL NULL NULL
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL NULL NULL

Example 4: Handling NULL input

Goal: Demonstrate the behavior when the function is provided with a NULL input.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter null_input_value = NULL
| alter converted_null_timestamp = timestamp_seconds(null_input_value)
| fields event_id, null_input_value, converted_null_timestamp
| limit 3

Explanation: The function returns NULL because the input is explicitly NULL.

Output:

EVENT_ID NULL_INPUT_VALUE CONVERTED_NULL_TIMESTAMP
101 NULL NULL
102 NULL NULL
103 NULL NULL

to_boolean

Use the to_boolean() function to explicitly transform string representations of boolean values into their native boolean data type.

Syntax

to_boolean (<string>)

Parameters

Name Type Required Description
string string Yes The string field or literal value that you want to convert.

Returns

The to_boolean() function returns a boolean value (true or false).

Usage notes

  • The input string must be either "TRUE" or "FALSE".
  • The conversion is case-insensitive, meaning "true", "TRUE", "True", "false", "FALSE", "False", etc., are all valid inputs for conversion.
  • If the input string is not "TRUE" or "FALSE" (case-insensitive), or if the input is NULL, the function will return NULL.

Examples

Example 1: Converting a literal string "TRUE"

Goal: Convert the literal string "TRUE" to a boolean true value.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter is_active_flag = to_boolean("TRUE")
| fields event_id, is_active_flag
| limit 3

Explanation: This query creates a new field, is_active_flag, which holds the boolean true value for every record, derived from the literal string "TRUE".

Output:

EVENT_ID IS_ACTIVE_FLAG
101 true
102 true
103 true

Example 2: Converting a literal string "FALSE"

Goal: Convert the literal string "FALSE" to a boolean false value.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter is_blocked_flag = to_boolean("FALSE")
| fields event_id, is_blocked_flag
| limit 3

Explanation: This query creates a new field, is_blocked_flag, which holds the boolean false value for every record, derived from the literal string "FALSE".

Output:

EVENT_ID IS_BLOCKED_FLAG
101 false
102 false
103 false

Example 3: Converting a case-insensitive literal string

Goal: Convert mixed-case literal strings (for example, "true", "FaLsE") to their boolean equivalents.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter status_boolean_lower = to_boolean("true")
| alter status_boolean_mixed = to_boolean("FaLsE")
| fields event_id, status_boolean_lower, status_boolean_mixed
| limit 3

Explanation: The query demonstrates that to_boolean() successfully interprets both "true" and "FaLsE" (case-insensitively) into their respective boolean true and false values.

Output:

EVENT_ID STATUS_BOOLEAN_LOWER STATUS_BOOLEAN_MIXED
101 true false
102 true false
103 true false

Example 4: Converting a derived string field

Goal: Convert a string field ("TRUE"/"FALSE") derived from existing data into a boolean type.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter success_string = if(is_successful = true, "TRUE", "FALSE")
| alter converted_success = to_boolean(success_string)
| fields event_id, is_successful, success_string, converted_success
| limit 3

Explanation: The if() function first creates success_string as either "TRUE" or "FALSE" based on is_successful. Then, to_boolean() correctly converts this string representation into a native boolean value in converted_success.

Output:

EVENT_ID IS_SUCCESSFUL SUCCESS_STRING CONVERTED_SUCCESS
101 true "TRUE" true
102 false "FALSE" false
103 true "TRUE" true

Example 5: Handling invalid string input

Goal: Demonstrate the behavior when attempting to convert an invalid string or a NULL value.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter invalid_conversion = to_boolean("not_a_boolean_string")
| alter null_input_conversion = to_boolean(NULL)
| fields event_id, invalid_conversion, null_input_conversion
| limit 3

Explanation: As expected for invalid conversions in XQL, both attempts result in NULL values for the new fields, demonstrating to_boolean()'s strict input requirements.

Output:

EVENT_ID INVALID_CONVERSION NULL_INPUT_CONVERSION
101 NULL NULL
102 NULL NULL
103 NULL NULL

Example 6: Using supported operators for deriving a boolean string

Goal: Use to_boolean() within a filter stage to evaluate a derived string field as a boolean condition (for example, checking if an event ID is odd).

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter division_result = divide(event_id, 2)
| alter is_event_active_string = if(division_result != floor(division_result), "TRUE", "FALSE")
| filter to_boolean(is_event_active_string) = true
| fields event_id, is_event_active_string
| limit 3

Explanation: This query calculates the remainder of dividing event_id by 2. If the result is not a whole number (meaning the ID is odd), it assigns "TRUE" to the string field. to_boolean() converts this string to a boolean for the filter, returning only records with odd event IDs.

Output:

EVENT_ID IS_EVENT_ACTIVE_STRING
101 "TRUE"
103 "TRUE"
105 "TRUE"

to_epoch

Use the to_epoch() function to convert a timestamp value into the Unix epoch timestamp format.

Syntax

to_epoch (<timestamp>, <time unit>)

Parameters

Name Type Required Description
timestamp timestamp Yes The timestamp value to convert. This must be a TIMESTAMP object, not a string.
time unit string Yes The granularity of the returned integer value. Supported values are SECONDS, MILLIS, or MICROS.

Returns

The to_epoch() function returns an integer representing the Unix epoch timestamp.

Usage notes

  • The first parameter must be a TIMESTAMP object. If you have a string representation of a timestamp, you must first convert it to a TIMESTAMP object using functions like parse_epoch() or to_timestamp().
  • The <time unit> parameter specifies the granularity of the returned integer value (seconds, milliseconds, or microseconds).
  • If no <time unit> is configured, SECONDS is used as the default.

Examples

Example 1: Converting _time (timestamp field) to epoch in seconds

Goal: Convert the _time field, which is already a TIMESTAMP object, to its Unix epoch representation in seconds.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter epoch_seconds = to_epoch(_time, "SECONDS")
| fields event_id, _time, epoch_seconds
| limit 3

Explanation: The _time field is converted to an epoch integer in seconds using the "SECONDS" unit.

Output:

EVENT_ID _TIME EPOCH_SECONDS
101 2023-10-26 10:00:00 UTC 1698304800
102 2023-10-26 10:05:30 UTC 1698305130
103 2023-10-26 10:15:15 UTC 1698305715

Example 2: Converting _time (timestamp field) to epoch in milliseconds

Goal: Convert the _time field to its Unix epoch representation in milliseconds.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter epoch_millis = to_epoch(_time, "MILLIS")
| fields event_id, _time, epoch_millis
| limit 3

Explanation: The _time field is converted to an epoch integer in milliseconds using the "MILLIS" unit.

Output:

EVENT_ID _TIME EPOCH_MILLIS
101 2023-10-26 10:00:00 UTC 1698304800000
102 2023-10-26 10:05:30 UTC 1698305130000
103 2023-10-26 10:15:15 UTC 1698305715000

Example 3: Converting _time (timestamp field) to epoch in microseconds

Goal: Convert the _time field to its Unix epoch representation in microseconds.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter epoch_micros = to_epoch(_time, "MICROS")
| fields event_id, _time, epoch_micros
| limit 3

Explanation: The _time field is converted to an epoch integer in microseconds using the "MICROS" unit.

Output:

EVENT_ID _TIME EPOCH_MICROS
101 2023-10-26 10:00:00 UTC 1698304800000000
102 2023-10-26 10:05:30 UTC 1698305130000000
103 2023-10-26 10:15:15 UTC 1698305715000000

Example 4: Converting a string timestamp to epoch

Goal: Convert a string that represents a timestamp into epoch time by first creating a TIMESTAMP object.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter literal_timestamp_string = "2023-10-26 10:00:00 UTC"
| alter parsed_timestamp_obj = parse_timestamp("%Y-%m-%d %H:%M:%S UTC", literal_timestamp_string)
| alter epoch_from_string = to_epoch(parsed_timestamp_obj, "SECONDS")
| fields event_id, literal_timestamp_string, parsed_timestamp_obj, epoch_from_string
| limit 3

Explanation: The query first uses parse_timestamp() to create a TIMESTAMP object from a string literal. Then, to_epoch() converts that object into epoch seconds.

Output:

EVENT_ID LITERAL_TIMESTAMP_STRING PARSED_TIMESTAMP_OBJ EPOCH_FROM_STRING
101 "2023-10-26 10:00:00 UTC" 2023-10-26 10:00:00 UTC 1698304800
102 "2023-10-26 10:00:00 UTC" 2023-10-26 10:00:00 UTC 1698304800
103 "2023-10-26 10:00:00 UTC" 2023-10-26 10:00:00 UTC 1698304800

to_float

Use the to_float() function to convert a string value that represents a number or a numeric data type into a floating-point number.

Syntax

to_float (<string>)

Parameters

Name Type Required Description
string string Yes The string field or literal value that you wish to convert to a number.

Returns

The to_float() function returns a floating-point number.

Usage notes

  • The function is functionally identical to the to_number() function.
  • The function consistently returns a floating-point number. Even if the input string represents a whole integer (for example, "200"), the output will be a floating-point number (for example, 200.0).
  • If the input string does not represent a valid number (for example, contains letters or unparseable characters), or if the input itself is NULL, the function will return NULL.

Examples

Example 1: Converting a literal string (decimal representation) to float

Goal: Convert a string literal that represents a decimal number directly into a floating-point number.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter float_from_decimal_string = to_float("1.5") 
| fields event_id, float_from_decimal_string 
| limit 3

Explanation: The literal string "1.5" is converted to a float.

Output:

EVENT_ID FLOAT_FROM_DECIMAL_STRING
101 1.5
102 1.5
103 1.5

Example 2: Converting a literal string (integer representation) to float

Goal: Convert a string literal that represents a whole number into its floating-point equivalent.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter float_from_integer_string = to_float("100") 
| fields event_id, float_from_integer_string 
| limit 3

Explanation: The literal string "100" is converted to the floating-point number 100.0.

Output:

EVENT_ID FLOAT_FROM_INTEGER_STRING
101 100.0
102 100.0
103 100.0

Example 3: Converting a string extracted from JSON to float

Goal: Extract a numeric value (code) from a JSON string field (simple_json_data) and convert that extracted string to a float.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter json_code_string = simple_json_data -> code 
| alter converted_json_code = to_float(json_code_string) 
| fields event_id, simple_json_data, json_code_string, converted_json_code 
| limit 3

Explanation: For event_id 101, the "code" value "200" is extracted as a string and converted to the number 200.0. For events where the field is missing, it returns NULL.

Output:

EVENT_ID SIMPLE_JSON_DATA JSON_CODE_STRING CONVERTED_JSON_CODE
101 {"status": "ok", "code": 200} "200" 200.0
102 {"status": "fail", "error": "access_denied"} NULL NULL
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL NULL

Example 4: Converting an integer field (via to_string()) to float

Goal: Convert an existing integer field (event_id) to a string first using to_string(), and then convert that string representation into a float.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter event_id_as_string = to_string(event_id) 
| alter float_event_id = to_float(event_id_as_string) 
| fields event_id, event_id_as_string, float_event_id 
| limit 3

Explanation: The numeric event_id (for example, 101) is converted to a string "101", and then back to a float 101.0.

Output:

EVENT_ID EVENT_ID_AS_STRING FLOAT_EVENT_ID
101 "101" 101.0
102 "102" 102.0
103 "103" 103.0

Example 5: Handling non-numeric string input (returns NULL)

Goal: Demonstrate the function's behavior when provided with a string that cannot be interpreted as a number (for example, a purely textual field like event_description).

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter non_numeric_string = event_description 
| alter float_conversion_result = to_float(non_numeric_string) 
| fields event_id, non_numeric_string, float_conversion_result 
| limit 3

Explanation: Since event_description contains text (for example, "User login successful"), to_float() returns NULL.

Output:

EVENT_ID NON_NUMERIC_STRING FLOAT_CONVERSION_RESULT
101 "User login successful" NULL
102 "File access attempt" NULL
103 "Network connection established" NULL

to_integer

Use the to_integer() function to convert a string representation of a number into an integer value.

Syntax

to_integer(<string>)

Parameters

Name Type Required Description
string string Yes The string field or literal value that you wish to convert to a number.

Returns

The to_integer() function returns an integer.

Usage notes

  • When a string representation of a floating-point number (for example, "1.5") is provided as input, the function returns NULL. This differentiates its behavior from functions like floor() or round().
  • If passed a numeric data type (like a float), the result will be rounded up or down to the nearest integer.
  • If the input string does not represent a valid number, or if the input itself is NULL, the function returns NULL.

Examples

Example 1: Converting a literal integer string to integer

Goal: Convert a string literal that represents a whole number directly into an integer.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter literal_integer_val = to_integer("42")
| fields event_id, literal_integer_val
| limit 3

Explanation: The literal string "42" is successfully converted by to_integer() into the integer value 42.

Output:

EVENT_ID LITERAL_INTEGER_VAL
101 42
102 42
103 42

Example 2: Converting a literal floating-point string to integer

Goal: Demonstrate behavior when provided with a string that represents a floating-point number.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter float_string_to_int = to_integer("123.789")
| fields event_id, float_string_to_int
| limit 3

Explanation: When a string containing a decimal (floating-point representation) is passed to to_integer(), it results in a NULL output.

Output:

EVENT_ID FLOAT_STRING_TO_INT
101 NULL
102 NULL
103 NULL

Example 3: Converting a numeric string from an existing field to integer

Goal: Convert an existing floating-point field to an integer, demonstrating automatic rounding.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter integer_duration = to_integer(duration_seconds)
| fields event_id, duration_seconds, integer_duration
| limit 3

Explanation: The duration_seconds field is a floating-point number. When to_integer() is applied to a numeric type, it automatically rounds the number to the nearest integer (for example, 1.5 becomes 2, 0.8 becomes 1).

Output:

EVENT_ID DURATION_SECONDS INTEGER_DURATION
101 1.5 2
102 0.8 1
103 10.2 10

Example 4: Converting a string extracted from JSON to integer

Goal: Extract a numeric value from a JSON string field and convert it to an integer.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter json_code_string = simple_json_data -> code
| alter parsed_json_code = to_integer(json_code_string)
| fields event_id, simple_json_data, json_code_string, parsed_json_code
| limit 3

Explanation: For event 101, the "code" value "200" is extracted as a string and converted to the integer 200. For events where the field is missing, the result is NULL.

Output:

EVENT_ID SIMPLE_JSON_DATA JSON_CODE_STRING PARSED_JSON_CODE
101 {"status": "ok", "code": 200} "200" 200
102 {"status": "fail", "error": "access_denied"} NULL NULL
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL NULL

Example 5: Handling non-numeric string input

Goal: Demonstrate behavior when providing a string that cannot be interpreted as a number.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter non_numeric_string = event_description
| alter int_conversion_result = to_integer(non_numeric_string)
| fields event_id, non_numeric_string, int_conversion_result
| limit 3

Explanation: The conversion returns NULL because event_description contains text (for example, "User login successful") and not a valid number.

Output:

EVENT_ID NON_NUMERIC_STRING INT_CONVERSION_RESULT
101 "User login successful" NULL
102 "File access attempt" NULL
103 "Network connection established" NULL

Example 6: Using to_integer() in a filter stage

Goal: Extract a numeric value from a string field, convert it to an integer, and use it for numerical comparison in a filter.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter first_octet_string = arrayindex(split(ipv4_address, "."), 0)
| alter first_octet_int = to_integer(first_octet_string)
| filter first_octet_int > 192
| fields event_id, ipv4_address, first_octet_string, first_octet_int
| limit 3

Explanation: The query splits the IP address to isolate the first octet as a string. to_integer() converts this string to an integer, allowing the filter stage to numerically compare if the octet is greater than 192.

Output:

EVENT_ID IPV4_ADDRESS FIRST_OCTET_STRING FIRST_OCTET_INT
106 "203.0.113.15" "203" 203

to_json_string

Use the to_json_string() function to convert values of any data type into a JSON formatted string.

Syntax

to_json_string(<data type>)

Parameters

Name Type Required Description
data type integer, boolean, string, object, array Yes The field or literal value to convert.

Returns

The to_json_string() function returns a string.

Usage notes

  • The function is versatile and accepts all data types, including integers, booleans, strings, objects, and arrays.
  • When the input is an object or an array, the function returns a JSON formatted string representation of that input, preserving its structure.
  • When the input is a simple string, it returns the string as its JSON literal representation (in other words, enclosed in quotes, with any internal special characters properly escaped if necessary).
  • For numeric or boolean inputs, it converts them into their JSON string literal representations (for example, a number 101 becomes the string "101", a boolean true becomes the string "true").
  • This function is particularly useful for standardizing data types for consistent processing or preparing complex data types for functions that require JSON string inputs, such as json_extract().

Examples

Example 1: Converting a string field

Goal: Convert the event_description string field into its JSON string representation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter description_as_json = to_json_string(event_description)
| fields event_id, event_description, description_as_json
| limit 3

Explanation: The to_json_string() function takes the event_description string and returns it formatted as a JSON string literal.

Output:

EVENT_ID EVENT_DESCRIPTION DESCRIPTION_AS_JSON
101 "User login successful" "User login successful"
102 "File access attempt" "File access attempt"
103 "Network connection established" "Network connection established"

Example 2: Converting a numeric field (integer)

Goal: Convert the event_id integer field into its JSON string representation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_id_as_json = to_json_string(event_id)
| fields event_id, event_id_as_json
| limit 3

Explanation: The integer event_id is converted into a string format suitable for JSON.

Output:

EVENT_ID EVENT_ID_AS_JSON
101 "101"
102 "102"
103 "103"

Example 3: Converting a numeric field (float)

Goal: Convert the duration_seconds floating-point number field into its JSON string representation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter duration_as_json = to_json_string(duration_seconds)
| fields event_id, duration_seconds, duration_as_json
| limit 3

Explanation: The floating-point duration_seconds field is converted into a string format.

Output:

EVENT_ID DURATION_SECONDS DURATION_AS_JSON
101 1.5 "1.5"
102 0.8 "0.8"
103 10.2 "10.2"

Example 4: Converting a boolean field

Goal: Convert the is_successful boolean field into its JSON string representation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter successful_as_json = to_json_string(is_successful)
| fields event_id, is_successful, successful_as_json
| limit 3

Explanation: The boolean is_successful values (true or false) are converted into their corresponding string representations.

Output:

EVENT_ID IS_SUCCESSFUL SUCCESSFUL_AS_JSON
101 true "true"
102 false "false"
103 true "true"

Example 5: Converting a JSON object field to a JSON string

Goal: Convert the simple_json_data field, which holds a JSON object, into a single JSON string.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter simple_data_as_json = to_json_string(simple_json_data)
| fields event_id, simple_json_data, simple_data_as_json
| limit 3

Explanation: The entire JSON object in simple_json_data is converted into a string. Note that the entire structure is enclosed in outer double quotes, and original double quotes within the JSON are escaped with a backslash (\") to ensure it is a valid JSON string literal.

Output:

EVENT_ID SIMPLE_JSON_DATA SIMPLE_DATA_AS_JSON
101 {"status": "ok", "code": 200} "{"status": "ok", "code": 200}"
102 {"status": "fail", "error": "access_denied"} "{"status": "fail", "error": "access_denied"}"
103 {"connection_id": "CONN-001", "protocol": "TCP"} "{"connection_id": "CONN-001", "protocol": "TCP"}"

Example 6: Converting an array of JSON objects field to a JSON string

Goal: Convert the array_of_json_objects field, which contains an array, into a JSON string.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter array_data_as_json = to_json_string(array_of_json_objects)
| fields event_id, array_of_json_objects, array_data_as_json
| limit 3

Explanation: The array structure, including its nested JSON objects, is converted into a single, correctly quoted and escaped string.

Output:

EVENT_ID ARRAY_OF_JSON_OBJECTS ARRAY_DATA_AS_JSON
101 [{"action": "read", "file": "doc1.txt"}, {"action": "write", "file": "report.log"}] "[{"action": "read", "file": "doc1.txt"}, {"action": "write", "file": "report.log"}]"
102 [{"event": "file_open", "path": "/etc/passwd"}] "[{"event": "file_open", "path": "/etc/passwd"}]"
103 [{"conn_type": "outbound", "bytes": 1024}, {"conn_type": "inbound", "bytes": 512}] "[{"conn_type": "outbound", "bytes": 1024}, {"conn_type": "inbound", "bytes": 512}]"

to_number

Use the to_number() function to convert a string value that represents a number or a numeric data type into a floating-point number.

Syntax

to_number (<string>)

Parameters

Name Type Required Description
string string Yes The string field or literal value that you wish to convert to a number.

Returns

The to_number() function returns a floating-point number.

Usage notes

  • The function requires a string value that represents a number or a numeric data type.
  • The function is functionally identical to the to_float() function.
  • The function consistently returns a floating-point number. Even if the input string represents a whole integer (for example, "200"), the output will be a floating-point number (for example, 200.0).
  • If the input string cannot be successfully converted to a numeric value (for example, it contains letters or unparseable characters), the function will return NULL.
  • If the input string itself is NULL, the function will return NULL.

Examples

Example 1: Converting a literal integer string

Goal: Convert a string literal representing an integer into a numerical (float) value.

XQL code:

config timeframe = 1d // Sets the query time context 
| dataset = sample_xql_raw // Specifies the dataset to use 
| alter converted_integer_string = to_number("42") // Converts a literal string "42" to a number 
| fields event_id, converted_integer_string // Selects relevant fields for display 
| limit 3 // Limits the output for brevity 

Explanation: The literal string "42" is successfully converted by to_number() into a floating-point number 42.0.

Output:

EVENT_ID CONVERTED_INTEGER_STRING
101 42.0
102 42.0
103 42.0

Example 2: Converting a literal floating-point string

Goal: Convert a string literal representing a decimal number into a floating-point value.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter converted_float_string = to_number("123.45") // Converts a literal string "123.45" to a number 
| fields event_id, converted_float_string 
| limit 3 

Explanation: The literal string "123.45" is correctly converted into the floating-point number 123.45.

Output:

EVENT_ID CONVERTED_FLOAT_STRING
101 123.45
102 123.45
103 123.45

Example 3: Converting a numeric string extracted from a JSON field

Goal: Extract a numerical value from a JSON string field and convert it from its string representation to a number.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter extracted_code_as_string = simple_json_data -> code // Extracts 'code' as a string 
| alter converted_json_code = to_number(extracted_code_as_string) // Converts the extracted string to a number 
| fields event_id, simple_json_data, converted_json_code 
| limit 3 

Explanation: For event_id 101, the "code" value "200" is extracted as a string and then successfully converted to the number 200.0. For event_id 102 and 103, where simple_json_data does not contain a "code" field, extracted_code_as_string becomes NULL, and subsequently, converted_json_code also becomes NULL.

Output:

EVENT_ID SIMPLE_JSON_DATA CONVERTED_JSON_CODE
101 {"status": "ok", "code": 200} 200.0
102 {"status": "fail", "error": "access_denied"} NULL
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL

Example 4: Attempting to convert a string with non-numeric characters

Goal: Demonstrate the behavior when attempting to convert a string that contains non-numerical characters.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter mem_free_string = nested_json_data -> system.mem_free // Extracts "80%" as a string 
| alter converted_mem_free = to_number(mem_free_string) // Attempts to convert "80%" to a number 
| fields event_id, nested_json_data, converted_mem_free 
| filter event_id = 104 // Focus on the relevant record for this example 
| limit 1 

Explanation: to_number() cannot perform the conversion and returns NULL for invalid inputs because "80%" is not a pure numeric string.

Output:

EVENT_ID NESTED_JSON_DATA CONVERTED_MEM_FREE
104 {"system": {"cpu_util": 0.15, "mem_free": "80%"}, "status": "active"} NULL

Example 5: Converting a NULL input field

Goal: Demonstrate the behavior when the input field is explicitly NULL.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw
| filter event_id = 105 // Focus on the relevant record for this example
| alter null_input_field = dst_domain // This field is NULL for some records (for example, event_id 105) 
| alter converted_null = to_number(null_input_field) // Attempts to convert a NULL field to a number 
| fields event_id, dst_domain, converted_null 
| limit 1 

Explanation: When the input to to_number() is NULL, the function consistently returns NULL.

Output:

EVENT_ID DST_DOMAIN CONVERTED_NULL
105 NULL NULL

to_string

Use the to_string() function to convert a non-string value (such as a number, float, or boolean) into its string representation.

Syntax

to_string (<field>)

Parameters

Name Type Required Description
field integer, float, boolean Yes The field or literal value that you wish to convert to a string.

Returns

The to_string() function returns a string data type.

Usage notes

  • The function supports converting numerical types (integers and floats) and boolean values into strings.
  • If the input field or literal is NULL, the function returns NULL.
  • This function is essential when a string input is required for other XQL functions (like concat(), format_string(), or split()) or for performing string-based comparisons in filter stages.

Examples

Example 1: Converting a numeric (integer) field

Goal: Convert an integer field (event_id) to its string representation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter converted_event_id = to_string(event_id)
| fields event_id, converted_event_id
| limit 3

Explanation: The numeric event_id (for example, 101) is successfully converted by to_string() into the string "101".

Output:

EVENT_ID CONVERTED_EVENT_ID
101 "101"
102 "102"
103 "103"

Example 2: Converting a numeric (float) field

Goal: Convert a floating-point field (duration_seconds) to its string representation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter converted_duration = to_string(duration_seconds)
| fields event_id, duration_seconds, converted_duration
| limit 3

Explanation: The floating-point duration_seconds (for example, 1.5) is converted by to_string() into the string "1.5".

Output:

EVENT_ID DURATION_SECONDS CONVERTED_DURATION
101 1.5 "1.5"
102 0.8 "0.8"
103 10.2 "10.2"

Example 3: Converting a boolean field

Goal: Convert a boolean field (is_successful) to its string representation.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter converted_status = to_string(is_successful)
| fields event_id, is_successful, converted_status
| limit 3

Explanation: The boolean is_successful value (for example, true) is converted by to_string() into the string "true".

Output:

EVENT_ID IS_SUCCESSFUL CONVERTED_STATUS
101 true "true"
102 false "false"
103 true "true"

Example 4: Converting a literal numeric value

Goal: Convert a literal integer and a literal float to their string representations.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter literal_int_to_string = to_string(99)
| alter literal_float_to_string = to_string(3.14)
| fields event_id, literal_int_to_string, literal_float_to_string
| limit 3

Explanation: Both the integer 99 and the float 3.14 are directly converted into their respective string representations.

Output:

EVENT_ID LITERAL_INT_TO_STRING LITERAL_FLOAT_TO_STRING
101 "99" "3.14"
102 "99" "3.14"
103 "99" "3.14"

Example 5: Using to_string() in a filter condition

Goal: Convert a field to a string to enable string-based comparison in a filter stage.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter to_string(event_id) = "101"
| fields event_id, event_description, is_successful
| limit 3

Explanation: The event_id field is converted to a string before being compared to the string literal "101", successfully filtering for the event with ID 101.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL
101 "User login successful" true

Example 6: Handling NULL input field

Goal: Demonstrate the behavior when the input field is explicitly NULL.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id = 105
| alter null_input_field = dst_domain
| alter converted_null = to_string(null_input_field)
| fields event_id, dst_domain, converted_null
| limit 1

Explanation: When the input to to_string() is NULL (for example, dst_domain for event ID 105), the function consistently returns NULL.

Output:

EVENT_ID DST_DOMAIN CONVERTED_NULL
105 NULL NULL

to_timestamp

Use the to_timestamp() function to convert an integer value representing Unix epoch time into a human-readable TIMESTAMP data type.

Syntax

to_timestamp (<integer>, <units>)

Parameters

Name Type Required Description
integer integer Yes The numerical value representing epoch time. This can be a literal integer or a field containing an integer.
units string No A string literal that specifies the unit of the epoch integer. Supported values are "SECONDS", "MILLIS" (milliseconds), or "MICROS" (microseconds). The default value is "SECONDS" if the parameter is omitted.

Returns

The to_timestamp() function returns a TIMESTAMP compatible value.

Usage notes

  • The function is essential when you need to display or use epoch-based numerical time representations as standard timestamps for analysis, filtering, or display.
  • If the <units> parameter is not supplied, it defaults to "SECONDS".
  • to_timestamp() is often used after a function like parse_epoch() (which converts a string representation of a timestamp into an epoch integer) or to_epoch() (which converts a timestamp into an epoch integer).
  • If the input integer field or literal is NULL, the function will return NULL.
  • If the integer input value does not match the units parameter (for example, a non-sensical value for the given unit), this function will return NULL.

Examples

Example 1: Converting a numeric field to a timestamp (default seconds unit)

Goal: Convert an integer field to a timestamp, relying on the default behavior to treat the value as epoch seconds.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter epoch_timestamp_default_seconds = to_timestamp(to_integer(event_id)) 
| fields event_id, epoch_timestamp_default_seconds 
| limit 3 

Explanation: The event_id is passed through to_integer() to guarantee data type compliance, and then converted by to_timestamp() into a TIMESTAMP value corresponding to the seconds after the Unix epoch because no unit was specified, defaulting to SECONDS.

Output:

EVENT_ID EPOCH_TIMESTAMP_DEFAULT_SECONDS
101 1970-01-01 00:01:41 UTC
102 1970-01-01 00:01:42 UTC
103 1970-01-01 00:01:43 UTC

Example 2: Converting a numeric field to a timestamp (explicit seconds unit)

Goal: Explicitly convert an integer field to a timestamp, treating its value as epoch seconds.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter epoch_timestamp_explicit_seconds = to_timestamp(to_integer(event_id), "SECONDS") 
| fields event_id, epoch_timestamp_explicit_seconds 
| limit 3 

Explanation: The event_id is passed through to_integer() to guarantee data type compliance, and is then explicitly converted using the "SECONDS" unit, yielding the same result as the default behavior.

Output:

EVENT_ID EPOCH_TIMESTAMP_EXPLICIT_SECONDS
101 1970-01-01 00:01:41 UTC
102 1970-01-01 00:01:42 UTC
103 1970-01-01 00:01:43 UTC

Example 3: Converting a literal integer to a timestamp (milliseconds unit)

Goal: Convert a static integer literal, representing a specific point in time in milliseconds, to a timestamp.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter literal_timestamp_millis = to_timestamp(1672531200000, "MILLIS") 
| fields event_id, literal_timestamp_millis 
| limit 3 

Explanation: The literal integer 1672531200000 is explicitly converted as milliseconds, resulting in the constant timestamp for January 1, 2023, 00:00:00 UTC across all records.

Output:

EVENT_ID LITERAL_TIMESTAMP_MILLIS
101 2023-01-01 00:00:00 UTC
102 2023-01-01 00:00:00 UTC
103 2023-01-01 00:00:00 UTC

Example 4: Converting a literal integer to a timestamp (microseconds unit)

Goal: Convert a static integer literal, representing a specific point in time in microseconds, to a timestamp.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter literal_timestamp_micros = to_timestamp(1672531200000000, "MICROS") 
| fields event_id, literal_timestamp_micros 
| limit 3 

Explanation: The literal integer 1672531200000000 is explicitly converted as microseconds, resulting in the constant timestamp for January 1, 2023, 00:00:00 UTC across all records.

Output:

EVENT_ID LITERAL_TIMESTAMP_MICROS
101 2023-01-01 00:00:00 UTC
102 2023-01-01 00:00:00 UTC
103 2023-01-01 00:00:00 UTC

Example 5: Converting a derived integer from an array field to a timestamp (milliseconds unit)

Goal: Extract an integer from an array, treat it as epoch milliseconds, and convert it to a timestamp.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter first_numeric_code = to_integer(arrayindex(numeric_codes, 0)) 
| alter derived_timestamp_micros = to_timestamp(first_numeric_code, "MILLIS") 
| fields event_id, numeric_codes, derived_timestamp_micros 
| limit 3 

Explanation: For each event, the first value from its numeric_codes array is extracted and explicitly converted to an integer. This integer value is then interpreted as milliseconds from the Unix epoch and converted into a timestamp. Note that negative epoch values represent times before 1970-01-01 00:00:00 UTC.

Output:

EVENT_ID NUMERIC_CODES DERIVED_TIMESTAMP_MICROS
101 [13, -47, 29, 82, -15] 1970-01-01 00:00:00.013 UTC
102 [-21, 56, 13, -88, 42] 1969-12-31 23:59:59.979 UTC
103 [90, -33, 7, 51, -62, 18] 1970-01-01 00:00:00.090 UTC

Example 6: Handling NULL input

Goal: Demonstrate behavior when the input integer is NULL.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter null_integer_field = NULL 
| alter converted_null_timestamp = to_timestamp(null_integer_field, "SECONDS") 
| fields event_id, converted_null_timestamp 
| limit 3 

Explanation: When the input to to_timestamp() is NULL, the function consistently returns NULL.

Output:

EVENT_ID CONVERTED_NULL_TIMESTAMP
101 NULL
102 NULL
103 NULL

trim

Use the trim() function to remove specified characters or whitespace from the beginning and end of a given string.

Syntax

trim (<string>, [trim_characters])

Parameters

Name Type Required Description
string string Yes The input string field or literal value to be trimmed.
trim_characters string No A string containing the characters you want to remove. If this parameter is omitted, the function will remove whitespace characters (spaces and tabs).

Returns

The trim() function returns a string with the specified characters or whitespace removed from both the start and end.

Usage notes

  • The function removes characters from both the beginning (left) and end (right) of the string.
  • If trim_characters is not provided, the function defaults to removing leading and trailing spaces and tabs.
  • The function is versatile for cleaning and standardizing string data within queries.

Examples

Example 1: Removing leading and trailing whitespace (default behavior)

Goal: Remove spaces from both ends of a literal string where no specific characters are defined.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter original_string = "   User login successful   " 
| alter trimmed_string = trim(original_string) 
| fields event_id, original_string, trimmed_string 
| limit 1 

Explanation: The query detects and removes the leading and trailing spaces from the original_string, resulting in a clean string "User login successful".

Output:

EVENT_ID ORIGINAL_STRING TRIMMED_STRING
101 " User login successful " "User login successful"

Example 2: Removing specific characters from both ends

Goal: Remove explicit characters ("w") from both ends of the dst_domain string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 103 
| alter domain_without_w = trim(dst_domain, "w")
| fields event_id, dst_domain, domain_without_w 

Explanation: The function removes all leading and trailing 'w' characters from the dst_domain string (for example, "www.google.com" becomes ".google.com").

Output:

EVENT_ID DST_DOMAIN DOMAIN_WITHOUT_W
103 "www.google.com" ".google.com"

Example 3: Removing multiple specific characters

Goal: Remove a set of explicit characters ("w", "m", and ".") from both ends of the dst_domain string.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| filter event_id = 110 
| alter domain_without_w_m_period = trim(dst_domain, "wm.")
| fields event_id, dst_domain, domain_without_w_m_period

Explanation: The function removes all occurrences of 'w', 'm', and '.' characters found at the start or end of the string. For "www.mongodb.com", the leading "www." and the trailing "m" are removed, leaving "ongodb.co".

Output:

EVENT_ID DST_DOMAIN DOMAIN_WITHOUT_W_M_PERIOD
110 "www.mongodb.com" "ongodb.co"

trunc

Use the trunc() function to truncate a numeric value to a specified number of decimal places by removing (not rounding) the extra digits.

Syntax

trunc(<number> [, <decimal_places>])

Parameters

Name Type Required Description
number integer, float Yes The numeric value to truncate.
decimal_places integer No The number of decimal places to keep. Defaults to 0 (truncate to integer). Negative values truncate digits to the left of the decimal point.

Returns

Type: float or integer

Description: The trunc() function returns the input value truncated to the specified number of decimal places. Unlike round(), which rounds to the nearest value, trunc() simply removes digits beyond the specified precision. If the input is null, the function returns null.

Usage notes

  • Input type: XQL doesn't support NaN or infinite values as input and these value types also can not be returned.
  • Truncation vs. Rounding: trunc() removes digits without rounding. For example, trunc(2.789, 1) returns 2.7, not 2.8.
  • Default Behavior: When decimal_places is omitted, the function truncates to 0 decimal places (i.e., returns the integer part).
  • Negative Decimal Places: A negative decimal_places value truncates digits to the left of the decimal point. For example, trunc(1234, -2) returns 1200.
  • Null Handling: If the input expression is null, the function returns null.
  • Direction: Truncation is always toward zero. For positive numbers, this is equivalent to floor(). For negative numbers, this is equivalent to ceil() (rounding toward zero, not toward negative infinity).
  • Common Use Cases: This function is typically used within the alter stage for precision control, data formatting, bucketing values, and removing unwanted decimal places.

Examples

Example 1: Truncate literal values to different precisions

Goal: Truncate a numeric literal to various decimal places to demonstrate the behavior.

XQL code:

dataset = xdr_data
| limit 1
| alter trunc0 = trunc(3.14159), trunc2 = trunc(3.14159, 2), trunc4 = trunc(3.14159, 4)
| fields trunc0, trunc2, trunc4

Explanation: trunc(3.14159) returns 3 (no decimal places), trunc(3.14159, 2) returns 3.14 (two decimal places), and trunc(3.14159, 4) returns 3.1415 (four decimal places). Note that the digits are removed, not rounded.

Output:

TRUNC0 TRUNC2 TRUNC4
3 3.14 3.1415

Example 2: Truncate field values to integer

Goal: Truncate floating-point field values to their integer part.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter truncated_duration = trunc(duration_seconds)
| fields event_id, duration_seconds, truncated_duration
| limit 3

Explanation: This query truncates each duration_seconds value to its integer part by removing the decimal portion. For example, 1.5 becomes 1 and 10.2 becomes 10. Unlike floor(), trunc() rounds toward zero, so -2.7 would become -2 (not -3).

Output:

EVENT_ID DURATION_SECONDS TRUNCATED_DURATION
101 1.5 1
102 0.8 0
103 10.2 10

Example 3: Truncate with negative decimal places

Goal: Truncate values to the nearest hundred using negative decimal places.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter truncated_id = trunc(event_id, -2)
| fields event_id, truncated_id
| limit 3

Explanation: Using trunc() with -2 decimal places truncates the last two digits of the integer, effectively rounding down to the nearest hundred. For example, 101 becomes 100 and 215 becomes 200.

Output:

EVENT_ID TRUNCATED_ID
101 100
102 100
103 100

Example 4: Compare trunc with round and floor for negative numbers

Goal: Demonstrate the difference between trunc(), round(), and floor() for negative numbers.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter neg_val = multiply(duration_seconds, -1)
| alter trunc_val = trunc(neg_val)
| alter round_val = round(neg_val)
| alter floor_val = floor(neg_val)
| fields event_id, neg_val, trunc_val, round_val, floor_val
| limit 3

Explanation: For negative numbers, trunc() rounds toward zero (for example, -1.5 becomes -1), round() rounds to the nearest integer (for example, -1.5 becomes -2), and floor() rounds toward negative infinity (for example, -1.5 becomes -2). This demonstrates the key behavioral difference between these functions.

Output:

EVENT_ID NEG_VAL TRUNC_VAL ROUND_VAL FLOOR_VAL
101 -1.5 -1 -2 -2
102 -0.8 0 -1 -1
103 -10.2 -10 -10 -11

uppercase

Use the uppercase() function to convert all characters in a given string field value or literal to their uppercase equivalents.

Syntax

uppercase (<string>)

Parameters

Name Type Required Description
<string> string Yes The input string field or literal value to be converted to uppercase.

Returns

The uppercase() function returns a string with all characters converted to uppercase.

Usage notes

  • The uppercase() function is typically used within the alter stage to create new fields or modify existing ones.
  • The function can also be used in filter stages, particularly in scenarios where case-insensitive comparisons are needed or to align with config case_sensitive = false behavior.

Examples

Example 1: uppercase() on a direct string field

Goal: Convert the event_description field to all uppercase letters.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter upper_description = uppercase(event_description)
| fields event_id, event_description, upper_description
| limit 3

Explanation: The uppercase() function converts the text in the event_description field to its all-uppercase equivalent, creating a new field upper_description.

Output:

event_id event_description upper_description
101 "User login successful" "USER LOGIN SUCCESSFUL"
102 "File access attempt" "FILE ACCESS ATTEMPT"
103 "Network connection established" "NETWORK CONNECTION ESTABLISHED"

Example 2: uppercase() on a string field (for example, Domain Name)

Goal: Standardize domain names in the dst_domain field to uppercase.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter upper_domain = uppercase(dst_domain)
| fields event_id, dst_domain, upper_domain
| limit 3

Explanation: The uppercase() function transforms the dst_domain strings, including any surrounding quotes, into their uppercase format in the upper_domain field.

Output:

event_id dst_domain upper_domain
101 "ec2.amazonaws.com" "EC2.AMAZONAWS.COM"
102 "sts.amazonaws.com" "STS.AMAZONAWS.COM"
103 "www.google.com" "WWW.GOOGLE.COM"

Example 3: uppercase() used in conjunction with filter

Goal: Convert raw_log_data to uppercase and then use the result in a filter stage to find specific patterns regardless of original case.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter upper_log_data = uppercase(raw_log_data)
| filter upper_log_data contains "PROCESS"
| fields event_id, raw_log_data, upper_log_data
| limit 2

Explanation: The raw_log_data is converted to upper_log_data, which is then used in a contains filter. This effectively performs a case-insensitive search for "process" by first normalizing the case.

Output:

event_id raw_log_data upper_log_data
102 "Process cmd.exe attempted to access /etc/passwd" "PROCESS CMD.EXE ATTEMPTED TO ACCESS /ETC/PASSWD"
105 "Transformed data from source X, processed 1000 records." "TRANSFORMED DATA FROM SOURCE X, PROCESSED 1000 RECORDS."

Example 4: uppercase() on an extracted JSON string field

Goal: Extract a string value from a JSON field (simple_json_data) and apply uppercase() to it.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter json_status_string = simple_json_data -> status
| alter uppercase_json_status = uppercase(json_status_string)
| fields event_id, simple_json_data, json_status_string, uppercase_json_status
| limit 3

Explanation: The status field is extracted from simple_json_data as a string (json_status_string), and then uppercase() converts this extracted value to uppercase for the uppercase_json_status field. Events where the status field does not exist will result in NULL.

Output:

event_id simple_json_data json_status_string uppercase_json_status
101 {"status": "ok", "code": 200} "ok" "OK"
102 {"status": "fail", "error": "access_denied"} "fail" "FAIL"
103 {"connection_id": "CONN-001", "protocol": "TCP"} NULL NULL

Example 5: uppercase() within arraymap() for an array of Strings

Goal: Convert every element within the string_tags array to uppercase using arraymap().

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter uppercase_tags = arraymap(string_tags, uppercase("@element"))
| fields event_id, string_tags, uppercase_tags
| limit 3

Explanation: The arraymap() function iterates through each element (@element) of the string_tags array, applying the uppercase() function to each one. The result is a new array, uppercase_tags, where all original elements are now in uppercase.

Output:

event_id string_tags uppercase_tags
101 ["security", "login"] ["SECURITY", "LOGIN"]
102 ["filesystem", "critical"] ["FILESYSTEM", "CRITICAL"]
103 ["network", "cloud"] ["NETWORK", "CLOUD"]

values

Use the values() function to collect all distinct (unique) values of a specified field across grouped rows within the comp stage and return them as an array. This is similar to ARRAY_AGG(DISTINCT ...) in SQL.

Syntax

| comp values(<field>) [by <group_field1>, <group_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
field any Yes The field whose distinct values will be collected into an array.
group_field any No One or more fields to group the results by. If omitted, all rows are treated as a single group.
alias string No An alias for the output field. If not specified, the output field name defaults to values_<field>.

Returns

Type: array

Description: The values() function returns an array containing all distinct (unique) values of the specified field within each group. NULL values may be excluded. The order of elements in the array is not guaranteed.

Usage notes

  • Distinct values: Unlike the list() function which collects all values including duplicates, values() returns only unique values.
  • Data types: Works with any data type (string, numeric, datetime, etc.).
  • Null handling: NULL values are typically excluded from the result array.
  • No ordering: The order of elements in the resulting array is not guaranteed.
  • No grouping: When used without a by clause, all rows are aggregated into a single group.
  • Use case: Useful for finding all unique values of a field within groups, such as all unique users who accessed a resource.

Examples

Example 1: Distinct source IPs per destination

Goal: Find all unique source IP addresses that connected to each destination IP.

XQL code:

dataset = xdr_data
| comp values(action_local_ip) by action_remote_ip as unique_source_ips

Explanation: The values() function collects all distinct action_local_ip values for each unique action_remote_ip, eliminating duplicate IPs.

Output:

ACTION_REMOTE_IP UNIQUE_SOURCE_IPS
10.0.0.1 [192.168.1.10, 192.168.1.20]
10.0.0.2 [192.168.1.30]

Example 2: All unique usernames across events

Goal: Find all distinct usernames across all events.

XQL code:

dataset = xdr_data
| comp values(actor_effective_username) as unique_users

Explanation: Without a by clause, the values() function collects all distinct actor_effective_username values across the entire dataset.

Output:

UNIQUE_USERS
[admin, user1, user2, svc_account]

Example 3: Distinct event types per host

Goal: Find all unique event types observed on each host.

XQL code:

dataset = xdr_data
| comp values(event_type) by agent_hostname as event_types, count(*) as total_events

Explanation: This query combines values() with count() to show both the distinct event types and the total event count per host.

Output:

AGENT_HOSTNAME EVENT_TYPES TOTAL_EVENTS
workstation-1 [NETWORK, PROCESS, FILE] 150
workstation-2 [NETWORK, REGISTRY] 85

var

Use the var() function to compute the variance of a specified numeric field across all rows in each group within the comp stage. Variance measures how far values are spread out from the mean. By default, var() computes the population variance. This is equivalent to VAR_POP in SQL.

Syntax

| comp var(<field>) [by <group_field1>, <group_field2>, ...] [as <alias>]

Parameters

Name Type Required Description
field numeric Yes The numeric field from which to compute the variance.
group_field any No One or more fields to group the results by. If omitted, all rows are treated as a single group.
alias string No An alias for the output field. If not specified, the output field name defaults to var_<field>.

Returns

Type: numeric (float)

Description: The var() function returns the population variance of the specified field within each group. Returns NULL if all values in the group are NULL. Returns 0 if there is only one non-NULL value.

Usage notes

  • Variance definition: Variance is the average of the squared differences from the mean: sum((x - mean)^2) / N.
  • Relationship to standard deviation: Variance is the square of the standard deviation. var(x) = stddev_population(x)^2.
  • Null handling: NULL values are ignored in the computation.
  • Data types: Only works with numeric fields.
  • Single value: If there is only one non-NULL value, the variance is 0.
  • Use case: Variance is useful for statistical analysis, anomaly detection, and understanding data distribution.

Examples

Example 1: Variance of response times per host

Goal: Compute the variance of response times for each host.

XQL code:

dataset = xdr_data
| comp var(action_total_time) by agent_hostname as response_variance

Explanation: The var() function computes the population variance of action_total_time for each unique agent_hostname, measuring how spread out the response times are from the mean.

Output:

AGENT_HOSTNAME RESPONSE_VARIANCE
workstation-1 155.00
workstation-2 69.22

Example 2: Overall variance of bytes transferred

Goal: Compute the variance of bytes transferred across all events.

XQL code:

dataset = xdr_data
| comp var(action_network_bytes_received) as bytes_variance

Explanation: Without a by clause, the var() function computes the population variance across all rows.

Output:

BYTES_VARIANCE
6035782.45

Example 3: Variance with mean and standard deviation

Goal: Compute variance alongside mean and standard deviation for comprehensive statistical analysis.

XQL code:

dataset = xdr_data
| comp var(action_total_time) as variance, avg(action_total_time) as mean, stddev_population(action_total_time) as stddev by agent_hostname

Explanation: This query combines var(), avg(), and stddev_population() to provide a complete statistical summary per host. Note that the variance equals the square of the standard deviation.

Output:

AGENT_HOSTNAME VARIANCE MEAN STDDEV
workstation-1 155.00 45.30 12.45
workstation-2 69.22 32.10 8.32

wildcard_match

Use the wildcard_match function to determine if a given string matches a specified wildcard pattern. The function is primarily used for flexible pattern matching beyond literal equality, often for filtering or categorizing data.

Syntax

wildcard_match(<string_value>, <wildcard_pattern>)

Parameters

Name Type Required Description
string_value string Yes The input string field or literal value to be evaluated against the pattern.
wildcard_pattern string Yes The pattern string, which can include wildcard characters * and ?.

Returns

The function returns a boolean value: true if the string matches the pattern, and false otherwise.

Usage notes

  • Availability: This function is only available with Cortex XSIAM licenses that include Cortex Cloud.
  • Wildcard characters:
  • * (Asterisk): Matches a sequence of zero or more (possibly different) characters.
  • ? (Question Mark): Matches exactly one character.
  • Case sensitivity: By default, the function is case-sensitive. To perform a case-insensitive match, you must add the (?i) syntax once at the beginning of the regular expression within the pattern string.
  • Stage usage: This function is typically used within the alter stage to create new boolean fields or directly within filter stages to narrow down results.

Examples

Example 1: Basic wildcard matching with *

Goal: Check if the event_description starts with "User" and ends with "successful".

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter matches_user_login = wildcard_match(event_description, "User*successful") 
| fields event_id, event_description, matches_user_login 
| limit 2 

Explanation: The function returns true for event 101 because "User login successful" matches the pattern "User*successful". The query returns false for event 102 because "File access attempt" does not match.

Output:

event_id event_description matches_user_login
101 "User login successful" true
102 "File access attempt" false

Example 2: Wildcard matching with ?

Goal: Use the ? wildcard to match exactly one character within the raw_log_data field, targeting a specific process name pattern like "c?d.exe".

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter matches_cmd_pattern = wildcard_match(raw_log_data, "Process c?d.exe*") 
| fields event_id, raw_log_data, matches_cmd_pattern 
| limit 2 

Explanation: For event 102, the string "Process cmd.exe..." matches the pattern because the 'm' in "cmd" is a single character matched by ?. Event 101 does not match the pattern.

Output:

event_id raw_log_data matches_cmd_pattern
101 "User Alice logged in from 192.168.1.10" false
102 "Process cmd.exe attempted to access /etc/passwd" true

Example 3: Complex pattern with * and ?

Goal: Combine both wildcards to match a complex file path pattern in raw_log_data.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter matches_complex_pattern = wildcard_match(raw_log_data, "*cmd.ex? attempted to access /*/passw?d") 
| fields event_id, raw_log_data, matches_complex_pattern 
| limit 2 

Explanation: The pattern uses * to match variable segments ("Process " and "tc/") and ? to match specific single characters ('e' in exe and 'd' in passwd). This correctly identifies event 102 as a match.

Output:

event_id raw_log_data matches_complex_pattern
101 "User Alice logged in from 192.168.1.10" false
102 "Process cmd.exe attempted to access /etc/passwd" true

Example 4: Case-insensitive matching

Goal: Perform a case-insensitive check on event_description using the (?i) flag.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| alter matches_case_insensitive = wildcard_match(event_description, "(?i)user*login*successful") 
| fields event_id, event_description, matches_case_insensitive 
| limit 2 

Explanation: Even though event_description starts with an uppercase "User" and the pattern specifies "user", the (?i) flag enables case-insensitive matching, resulting in true for event 101.

Output:

event_id event_description matches_case_insensitive
101 "User login successful" true
102 "File access attempt" false

Stages

XQL stages define how data flows through a query pipeline.

Use this section to:

Stages overview

XQL pipeline stages define query structure and control query flow.

Each stage performs one operation. Stages connect with the pipe (|) operator.

Stages list

The following table lists all available XQL pipeline stages:

Stage Description
alter Manipulate data by changing existing field values or creating new fields
arrayexpand Expand an array field into multiple rows
bin Group numeric values into bins
call Call a saved XQL query
comp Aggregate data using grouping and aggregate functions
config Configure query settings
dataset Specify the dataset to query
dedup Remove duplicate rows
fields Select or exclude specific fields
filter Filter rows based on a condition
iploc Enrich IP addresses with geolocation data
join Join two datasets
limit Limit the number of rows returned
pivot Rotate row-level data into columns using aggregate functions
preset Apply preset configurations
replacenull Replace null values with a specified value
search Search for records matching a text query
sort Sort rows by one or more fields
tag Add tags to query results
target Specify the target output for query results
top Return the top N rows by a specified field
transaction Group events into transactions
transpose Transpose rows into columns
union Combine the results of two or more queries
view Query a saved view
windowcomp Perform analytic (window) functions over a defined window of rows without collapsing the dataset
Stage Description
alter Manipulate data by changing existing field values or creating new fields
arrayexpand Expand an array field into multiple rows
bin Group numeric values into bins
call Call a saved XQL query
comp Aggregate data using grouping and aggregate functions
config Configure query settings
dataset Specify the dataset to query
dedup Remove duplicate rows
fields Select or exclude specific fields
filter Filter rows based on a condition
iploc Enrich IP addresses with geolocation data
join Join two datasets
limit Limit the number of rows returned
pivot Rotate row-level data into columns using aggregate functions
preset Apply preset configurations
replacenull Replace null values with a specified value
search Perform free-text searches across ingested data
sort Sort rows by one or more fields
tag Add tags to query results
target Specify the target output for query results
top Return the top N rows by a specified field
transaction Group events into transactions
transpose Transpose rows into columns
union Combine the results of two or more queries
view Query a saved view
windowcomp Perform analytic (window) functions over a defined window of rows without collapsing the dataset

alter

Use the alter stage to manipulate data by changing existing field values or creating new fields based on constants, other fields, or the results of various XQL functions.

Syntax

alter <field1> = <function value1> [, <field2> = <function_value2>, ...]

Parameters

Name Type Required Description
<field> string Yes The name of the field to create or modify.
<function_value> various Yes The constant, field reference, or function result to assign to the field.

Returns

The alter stage returns the dataset with the specified fields modified or added. New columns created by the alter stage are added as the last columns in the result set.

Usage notes

  • New fields and field values can only be created in the alter stage. No other stage allows for the creation of new fields and/or values.
  • After defining or modifying a field with alter, you can apply other stages, such as filter, to this new or modified field.
  • New columns created by the alter stage are added as the last columns in the result set. If a specific column order is desired, it can be adjusted using the fields stage later in the query.
  • XQL supports single ("<text>") and triple ("""<text>""") double quotes for defining string fields.
  • Single quotes treat values literally, while triple quotes process escape sequences (like \t for tab) and escaped backslashes. Choose the appropriate quoting based on your desired interpretation of special characters.

Examples

Example 1: Creating a new field with a constant value

Goal: Add a new column with a fixed value for all records.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter analysis_tag = "initial_review"
| fields event_id, analysis_tag

Explanation: A new field analysis_tag is created, and the value "initial_review" is assigned to every record.

Output:

EVENT_ID ANALYSIS_TAG
101 initial_review
102 initial_review
103 initial_review

Example 2: Creating a new field based on an existing field

Goal: Duplicate a field or create a new field that directly references an existing one.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter replicated_description = event_description
| fields event_id, replicated_description

Explanation: The replicated_description field is created, mirroring the event_description field.

Output:

EVENT_ID REPLICATED_DESCRIPTION
101 User login successful
102 File access attempt
103 Network connection established

Example 3: Modifying an existing field

Goal: Overwrite the current value of a known field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_description = concat("Processed: ", event_description)
| fields event_id, event_description

Explanation: The original event_description is updated by prepending "Processed: " to its value.

Output:

EVENT_ID EVENT_DESCRIPTION
101 Processed: User login successful
102 Processed: File access attempt
103 Processed: Network connection established

Example 4: Using string manipulation functions (concat)

Goal: Join multiple strings together to reformat text data.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter event_summary = concat(event_description, " - Success: ", to_string(is_successful))
| fields event_id, event_summary

Explanation*: event_summary is created by concatenating the event description, a static string, and the boolean is_successful converted to a string.

Output:

EVENT_ID EVENT_SUMMARY
101 User login successful - Success: true
102 File access attempt - Success: false
103 Network connection established - Success: true

Example 5: Using string manipulation functions (lowercase)

Goal: Convert a string to all lowercase letters.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter lower_case_log = lowercase(raw_log_data)
| fields event_id, lower_case_log

Explanation: The raw_log_data field's content is converted to lowercase and stored in lower_case_log.

Output:

EVENT_ID LOWER_CASE_LOG
101 user alice logged in from 192.168.1.10
102 process cmd.exe attempted to access /etc/passwd

Example 6: Using string manipulation functions (split)

Goal: Divide a string into an array of substrings based on a delimiter.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter log_words = split(raw_log_data, " ")
| fields event_id, log_words

Explanation: The raw_log_data string is split into an array of words based on spaces, creating the log_words field.

Output:

EVENT_ID LOG_WORDS
101 ["User", "Alice", "logged", "in", "from", "192.168.1.10"]
102 ["Process", "cmd.exe", "attempted", "to", "access", "/etc/passwd"]

Example 7: Using JSON functions (json_extract_scalar)

Goal: Extract a single scalar value (for example, string, number, boolean) from a JSON object.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter json_status = json_extract_scalar(simple_json_data, "$.status")
| fields event_id, json_status

Explanation: Extracts the value of the "status" key from the simple_json_data JSON object into json_status.

Output:

EVENT_ID JSON_STATUS
101 ok
102 fail

Example 8: Using JSON functions (json_extract_array)

Goal: Convert a JSON array string to an XQL-native array.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter parsed_actions = json_extract_array(array_of_json_objects, "$")
| fields event_id, parsed_actions

Explanation: Converts the array_of_json_objects string (which contains a JSON array) into a native XQL array stored in parsed_actions.

Output:

EVENT_ID PARSED_ACTIONS
101 [{"action": "read", "file": "doc1.txt"}, {"action": "write", "file": "report.log"}]
102 [{"event": "file_open", "path": "/etc/passwd"}]

Example 9: Using conditional and null handling functions (if)

Goal: Apply logic to assign values based on conditions.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter outcome_category = if(is_successful, "Operational", "Alertable")
| fields event_id, outcome_category

Explanation: If is_successful is true, creates outcome_category as "Operational". If is_successful is false, creates outcome_category as "Alertable".

Output:

EVENT_ID OUTCOME_CATEGORY
101 Operational
102 Alertable
103 Operational

Example 10: Using conditional and null handling functions (coalesce)

Goal: Return the first non-NULL expression among its arguments.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter resolved_ip = coalesce(ipv4_address, ipv6_address)
| fields event_id, resolved_ip

Explanation: Assigns the value of ipv4_address to resolved_ip. If ipv4_address is NULL, the field is assigned ipv6_address instead.

Output:

EVENT_ID RESOLVED_IP
101 192.168.1.10
103 2001:0db8::1

Example 11: Using time functions (format_timestamp)

Goal: Format a timestamp into a specified string format.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter formatted_timestamp = format_timestamp("%Y-%m-%d %H:%M:%S", _time)
| fields event_id, formatted_timestamp

Explanation: Converts the _time field into a readable string format YYYY-MM-DD HH:MM:SS.

Output:

EVENT_ID FORMATTED_TIMESTAMP
101 2023-10-26 10:00:00
102 2023-10-26 10:05:30

Example 12: Using mathematical functions (add)

Goal: Add two numerical values.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter adjusted_duration = add(duration_seconds, 5.0)
| fields event_id, adjusted_duration

Explanation: Adds 5.0 to duration_seconds for each record, storing the result in adjusted_duration.

Output:

EVENT_ID ADJUSTED_DURATION
101 6.5
102 5.8

Example 13: Using mathematical functions (multiply)

Goal: Multiply two numerical values.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter doubled_duration = multiply(duration_seconds, 2)
| fields event_id, doubled_duration

Explanation: Multiplies duration_seconds by 2, storing the result in doubled_duration.

Output:

EVENT_ID DOUBLED_DURATION
101 3.0
102 1.6

Example 14: Using mathematical functions (divide)

Goal: Perform division on numerical values.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter duration_in_minutes = divide(duration_seconds, 60)
| fields event_id, duration_in_minutes

Explanation: Converts duration_seconds to minutes by dividing by 60, storing the result in duration_in_minutes.

Output:

EVENT_ID DURATION_IN_MINUTES
101 0.025
102 0.0133

Example 15: Using array functions (arraycreate)

Goal: Form a new array from specified values.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter default_tags = arraycreate("default_tag", "processed")
| fields event_id, default_tags

Explanation: Creates a new array default_tags containing the specified string values for each record.

Output:

EVENT_ID DEFAULT_TAGS
101 ["default_tag", "processed"]
102 ["default_tag", "processed"]

Example 16: Using array functions (arraymap)

Goal: Apply a function to each element of an array.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter positive_codes = arraymap(numeric_codes, if("@element" < 0, 0, "@element"))
| fields event_id, positive_codes

Explanation: Creates positive_codes by going through the array numeric_codes and replacing any negative values with 0.

Output:

EVENT_ID POSITIVE_CODES
101 [13, 0, 29, 82, 0]
102 [0, 56, 13, 0, 42]

Example 17: Using array functions (arrayrange)

Goal: Generate a portion of an array based on specified indices.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter first_two_tags = arrayrange(string_tags, 0, 2)
| fields event_id, first_two_tags

Explanation: Extracts the first two elements (indices 0 and 1) from the string_tags array into first_two_tags.

Output:

EVENT_ID FIRST_TWO_TAGS
101 ["security", "login"]
102 ["filesystem", "critical"]

arrayexpand

Use the arrayexpand stage to expand the values of a multi-value array field into separate events, creating a new record in the result set for each item present in the array.This is particularly useful when you need to analyze or filter individual elements within an array as if they were separate rows of data.

Syntax

arrayexpand <array_field> [limit <limit_number>]

Parameters

Name Type Required Description
array_field string Yes The name of the array field you wish to expand.
limit_number integer No Specifies the maximum number of records to create from the array expansion. If an array has more elements than this limit, only up to the specified number of records will be generated from its expansion.

Returns

The arrayexpand stage returns a dataset where, for each element in the specified array field, a new row is generated in the result set, duplicating all other fields from the original row.

Usage notes

  • The records created by arrayexpand are returned in no particular order.
  • If a specific order is required, you must apply a sort stage after arrayexpand.
  • The expansion duplicates all other fields from the original row for every new record created.

Examples

Example 1: Basic arrayexpand on a simple array

Goal: Expand the string_tags array into separate records. XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, string_tags 
| arrayexpand string_tags
| limit 5 

Explanation: For each original row in sample_xql_raw that has values in string_tags (for example, event ID 101 has ["security", "login"]), arrayexpand creates a separate row for each tag. Event ID 101 results in two rows: one for "security" and one for "login", each retaining the original event_id.

Output:

event_id string_tags
101 security
101 login
102 filesystem
102 critical
103 network

Example 2: arrayexpand with limit

Goal: Expand the numeric_codes array but restrict the number of expanded records for each original row to a specified limit.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, numeric_codes 
| arrayexpand numeric_codes limit 2 
| limit 5 

Explanation: For original rows like event ID 101, which has numeric_codes containing 5 elements, this query creates new records for only the first two elements. If the array has fewer than two elements, the query creates records for all existing elements.

Output:

event_id numeric_codes
101 13
101 -47
102 -21
102 56
103 90

Example 3: arrayexpand followed by sort

Goal: Expand string_tags and then sort the resulting records by the expanded tag value.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, string_tags 
| arrayexpand string_tags
| sort asc string_tags 
| limit 5 

Explanation: This query first expands the string_tags array for each event. Then, it sorts all the generated records based on the string_tags field (which now holds individual tag values), ensuring the final output is ordered alphabetically by tag.

Output:

event_id string_tags
103 cloud
102 critical
105 data_ops
102 filesystem
101 login

Example 4: arrayexpand on an array of JSON objects

Goal: Expand an array where each element is a JSON object, then extract a specific scalar value from those expanded objects.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter array_length(array_of_json_objects) > 0 
| fields event_id, array_of_json_objects 
| arrayexpand array_of_json_objects 
| alter action_type = json_extract_scalar(array_of_json_objects, "$.action") 
| fields event_id, action_type 
| limit 5

Explanation: For an event like ID 101, which has an array of JSON objects, this query first expands the array into separate records. Then, for each of these new records, it extracts the value associated with the "action" key using json_extract_scalar(), creating a new action_type field.

Output:

event_id action_type
101 read
101 write
106 NULL
106 NULL
105 NULL

bin

Use the bin stage to discretize a continuous numerical field or timestamp into separate buckets, or "bins." This is commonly used to group data into ranges, like age groups or time intervals, for aggregation and analysis.

Syntax

You can add the bin stage to your queries using two different formats, depending on whether you are grouping events by quantity or by time span.

Quantity:

bin <field_name> bins=<number>

Time span:

bin <field_name> span=<number><timescale> [timeshift=<epoch time> [timezone="<time zone>"]]

Parameters

Name Type Required Description
field_name string Yes The name of the numeric or timestamp field to be binned. When grouping by quantity, the field must be a number. When grouping by time, the field must be a date type. Otherwise, the query fails.
bins number No The maximum number of bins to divide the data into. Instead of specifying a fixed bin size with span, use bins to let the stage automatically calculate an appropriate bin size so that the values are distributed across (at most) the specified number of bins. Provide either span or bins, but not both.
span string No The size of each bin. For timestamps, this is a number followed by a timescale suffix (for example, 1h, 15m, 1d). See Time suffixes for the list of supported suffixes. For numeric fields, this is an integer representing the interval size. Provide either span or bins, but not both.
timeshift number No A start time for grouping the events, expressed as a Unix epoch time. Use with span when binning by time. If not set, the query runs according to the last time set in the log.
timezone string No The time zone applied when grouping events by time. Configure it using an hours offset, such as "+08:00", or using a time zone name from the List of Supported Time Zones, such as "America/Chicago". Optionally used together with timeshift.

Time suffixes

When binning by time span, <span> is a combination of a number and one of the following time suffixes. The time suffix is not case sensitive.

Time suffix Description
MS milliseconds
S seconds
M minutes
H hours
D days
W weeks
MO months
Y years

Returns

The bin stage modifies the specified field in the dataset, replacing the original continuous values with the starting value of the bin interval they fall into. The resulting field retains its original name.

Usage notes

  • The bin stage is often used immediately before an aggregation stage like comp to group records by time or numeric ranges. The most common use case is for timecharts.
  • Binning simplifies complex continuous data by separating it into manageable categories.
  • When binning timestamps, the returned value is the start time of the interval. For example, with span=1h, a timestamp of 10:45 becomes 10:00.
  • The span parameter determines the granularity of the groups.
  • You must specify either span or bins, but not both. Use span when you know the exact bin size you want, and use bins when you want a specific number of buckets and want the stage to determine the bin size automatically.
  • When you use bins, the stage calculates a bin size that distributes the values across at most the specified number of bins, so the actual number of populated bins can be fewer.
  • When you group events by quantity, the binned field must be a number. When you group events by time, the binned field must be a date type. Otherwise, the query fails.
  • The bin stage is only supported using the equal sign (=) operator, without any boolean operators (and, or).
  • Use the optional timeshift parameter to define a start time for grouping the events according to the Unix epoch time, and use the optional timezone parameter to apply a specific time zone. If neither is set, the query runs according to the last time set in the log.

Examples

Example 1: Binning by time

Goal: Group events into 1-hour intervals to prepare for counting events per hour.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| bin _time span = 1h
| comp count(event_id) as events_per_hour by _time
| fields _time, events_per_hour

Explanation: The _time field is binned into 1-hour buckets. The comp stage then counts the number of events that fall into each hour, creating a timeline of activity.

Output:

_time events_per_hour
2023-10-26 10:00:00 45
2023-10-26 11:00:00 32
2023-10-26 12:00:00 50

Example 2: Binning numeric values

Goal: Group file sizes into buckets of 100MB to analyze the distribution of file sizes.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter file_size_mb = divide(file_size, 1024 * 1024)
| bin file_size_mb span = 100
| comp count(event_id) as file_count by file_size_mb
| fields file_size_mb, file_count

Explanation: First, file size is converted to megabytes. Then, bin groups these sizes into 100MB intervals (0-100, 100-200, etc.). The comp stage counts how many files fall into each size range.

Output:

file_size_mb file_count
0 150
100 40
200 12

Example 3: Binning into a fixed number of buckets

Goal: Group file sizes into at most 5 buckets without specifying an exact bin size, letting the stage calculate the interval automatically.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| alter file_size_mb = divide(file_size, 1024 * 1024)
| bin file_size_mb bins = 5
| comp count(event_id) as file_count by file_size_mb
| fields file_size_mb, file_count

Explanation: Instead of defining a fixed interval with span, the bins = 5 parameter instructs the stage to divide the range of file_size_mb values into at most 5 buckets, automatically calculating an appropriate bin size. The comp stage then counts how many files fall into each bucket.

Output:

file_size_mb file_count
0 120
50 60
100 15
150 5
200 2

Example 4: Binning by time with a time zone offset

Goal: Group events into 1-hour intervals starting from a specific epoch time, using a time zone configured with an hours offset.

XQL code:

dataset = xdr_data
| bin _time span = 1h timeshift = 1615353499 timezone = "+08:00"
| limit 1000

Explanation: The _time field is grouped into 1-hour increments starting from the Unix epoch time 1615353499, applying a time zone offset of +08:00. The limit stage returns a maximum of 1,000 records.

Example 5: Binning by time with a time zone name

Goal: Group events into 1-hour intervals starting from a specific epoch time, using a named time zone.

XQL code:

dataset = xdr_data
| bin _time span = 1h timeshift = 1615353499 timezone = "America/Los_Angeles"
| limit 1000

Explanation: The _time field is grouped into 1-hour increments starting from the Unix epoch time 1615353499, applying the America/Los_Angeles time zone. The limit stage returns a maximum of 1,000 records.

  • Stages: comp, alter
  • Functions: count, divide

call

Use the call stage to reference and execute a saved query from the Query Library as a sub-query within your current XQL query. This allows you to modularize complex logic, reuse common query patterns, and maintain cleaner query structures.

Syntax

call <saved_query_name>

Parameters

Name Type Required Description
saved_query_name string Yes The exact name of the saved query in the Query Library that you want to execute. If the name contains spaces, it must be enclosed in double quotes (for example, "My Saved Query").

Returns

The call stage returns the dataset produced by the execution of the referenced saved query. The subsequent stages in your main query will process this returned dataset.

Usage notes

  • The call stage effectively replaces itself with the full XQL string of the saved query at execution time.
  • It is best practice to use call at the beginning of your query pipeline to fetch a prepared dataset, though it can be used wherever a dataset transformation is valid, provided the saved query's output is compatible with the preceding stages.
  • Recursive calls (a query calling itself) are not supported.
  • Ensure the saved query you are calling exists in the Query Library and that you have permission to view it.
  • If the saved query name changes, you must update the call statement in your queries to reflect the new name.

Examples

Example 1: Calling a saved query

Goal: Use a pre-defined saved query named "Failed Logins Last 24h" to start an investigation.

XQL code:

call "Failed Logins Last 24h"
| filter user_name != "admin"
| fields _time, user_name, src_ip

Explanation: The query first executes the saved query "Failed Logins Last 24h". The results of that query are then passed to the filter stage, which removes records where the user is "admin", and finally selects specific fields for display.

Output: The output depends entirely on the data returned by the saved query and the subsequent filter.

Example 2: Reusing data preparation logic

Goal: Reuse a standardized data cleaning query named clean_xdr_data before performing a specific aggregation.

XQL code:

call clean_xdr_data
| comp count(event_id) by agent_os_type

Explanation: The clean_xdr_data saved query is executed first (likely filtering out noise and normalizing fields). The resulting "clean" dataset is then aggregated to count events by OS type.

Output:

agent_os_type count
Windows 1500
Linux 450
macOS 200

comp

Use the comp stage (short for computation) to perform aggregation functions on your dataset. This stage groups records based on specified fields and calculates summary statistics�such as counts, averages, or sums�for each group. The stage is essential for transforming raw event data into meaningful metrics and trends.

Syntax

comp <function1>(<field1>) [as <alias1>], [<function2>(<field2>) [as <alias2>], ...] by <group_field1> [, <group_field2>, ...]

Parameters

Name Type Required Description
function function Yes The aggregation function to apply (for example, count, sum, avg, max, min, values).
field string Yes The name of the field to be aggregated.
alias string No An optional name for the resulting aggregated field. If omitted, a default name is generated (usually function_field).
group_field string Yes The field(s) used to group the results. All records with the same value in the group_field are calculated together.

Returns

The comp stage returns a summary dataset where each row represents a unique combination of the by (grouping) fields. The columns include the grouping fields and the results of the aggregation functions. All other original fields are discarded.

Usage notes

  • The comp stage is a "blocking" operation; it must process all input records before producing any output.
  • Common aggregation functions include count, sum, avg, min, max, count_distinct, and values.
  • You can group by multiple fields (for example, by user, host), which creates a separate row for every unique pair of user and host.
  • It is highly recommended to provide an alias (using as) for aggregated columns to make the output readable and easier to reference in later stages.
  • If you need to group by time, consider using the bin stage on a timestamp field before the comp stage.

Examples

Example 1: Basic Counting

Goal: Count the number of events associated with each user. XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp count(event_id) as total_events by user_name
| fields user_name, total_events

Explanation: This query groups all records by user_name and counts the number of event_ids for each user, storing the result in total_events. Output:

user_name total_events
alice 150
bob 42
charlie 5

Example 2: Multiple Aggregations

Goal: Find the first and last occurrence of an event for each IP address. XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp min(_time) as first_seen, max(_time) as last_seen by src_ip
| fields src_ip, first_seen, last_seen

Explanation: Grouping by src_ip, the query calculates the minimum (earliest) timestamp and maximum (latest) timestamp for each IP address. Output:

src_ip first_seen last_seen
192.168.1.5 2023-10-26 08:00:00 2023-10-26 17:30:00
10.0.0.42 2023-10-26 09:15:00 2023-10-26 09:20:00

Example 3: Grouping by Time (after Binning)

Goal: Count the number of failed logins per hour. XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_type = "login_failed"
| bin _time span = 1h
| comp count(event_id) as failure_count by _time
| fields _time, failure_count

Explanation: The bin stage buckets the timestamps into 1-hour intervals. The comp stage then groups by these 1-hour buckets to count the failed login events. Output:

_time failure_count
2023-10-26 10:00:00 12
2023-10-26 11:00:00 8
2023-10-26 12:00:00 25

Example 4: Collecting Unique Values

Goal: List all unique applications used by each host. XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp values(app_name) as distinct_apps by host_name
| fields host_name, distinct_apps

Explanation: The values function collects a list of all unique app_name entries for each host_name. Output:

host_name distinct_apps
server-01 ["nginx", "ssh", "python"]
laptop-99 ["chrome", "slack"]


              

config

Use the config stage to configure the behavior of your query, specifically controlling case sensitivity and the execution time frame. This stage is crucial for query optimization and must always be the very first stage in your XQL query.

Syntax

config case_sensitive = true | false
config timeframe = <number><time unit>
config timeframe between <start> and <end>

Parameters

Name Type Required Description
case_sensitive boolean No Controls whether field values are evaluated in a case-sensitive manner. Default is false.
timeframe string No Defines the specific time range for the query's execution.
number integer Yes (if timeframe used) The duration value for the timeframe.
time_unit string Yes (if timeframe used) The unit of time (S, M, H, D, W, MO, Y). Not case-sensitive.
start string Yes (if between used) The start of the time range. Can be a relative offset (for example, "-1h"), "begin", or an exact timestamp.
end string Yes (if between used) The end of the time range. Can be a relative offset, "now", or an exact timestamp.

Returns

The config stage does not return a value itself but sets the execution context (rules and scope) for the subsequent query stages.

Usage notes

  • The config stage must always be the first stage in your XQL query.
  • For case_sensitive, if not explicitly set, XQL queries operate with case-insensitivity (false) by default.
  • For timeframe, available time units are: S (seconds), M (minutes), H (hours), D (days), W (weeks), MO (months), Y (years). These are not case-sensitive.
  • While the Query Builder's time picker automatically sets the timeframe, it will only appear in your query text if you manually type the config timeframe command.

Examples

Goal: Configure the query to strictly match the case of string values during filtering.

XQL code:

config case_sensitive = true
| dataset = sample_xql_raw
| filter event_description = "user login successful"
| fields event_id, event_description
| limit 5

Explanation: The query attempts to find records where event_description is exactly "user login successful" (lowercase). Given the dataset contains "User login successful" (uppercase 'U'), this query yields no results because case_sensitive is set to true.

Output:

EVENT_ID EVENT_DESCRIPTION
  (No results)

Goal: Configure the query to ignore case differences when matching string values.

XQL code:

config case_sensitive = false
| dataset = sample_xql_raw
| filter event_description = "user login successful"
| fields event_id, event_description
| limit 5

Explanation: With case_sensitive set to false, the query successfully matches the lowercase string "user login successful" to the dataset's "User login successful", retrieving the record.

Output:

EVENT_ID EVENT_DESCRIPTION
101 User login successful

Example 3: Relative time (simple duration)

Goal: Retrieve records from the specified duration counting back from the current query execution time.

XQL code:

config timeframe = 1h
| dataset = sample_xql_raw
| fields _time, event_id, event_description
| limit 10

Explanation: If executed at 2023-10-26 10:30:00 UTC, this query retrieves events from the last hour (09:30:00 to 10:30:00 UTC), essentially capturing events 101 through 105 from the sample data.

Output:

_TIME EVENT_ID EVENT_DESCRIPTION
2023-10-26 10:00:00 UTC 101 User login successful
2023-10-26 10:05:30 UTC 102 File access attempt
2023-10-26 10:15:15 UTC 103 Network connection established
2023-10-26 10:20:00 UTC 104 System heartbeat
2023-10-26 10:30:45 UTC 105 Data transformation

Example 4: Relative time (range ending now)

Goal: Retrieve records within a range starting from a relative point in the past and ending at the current time.

XQL code:

config timeframe between "-30m" and "now"
| dataset = sample_xql_raw
| fields _time, event_id, event_description
| limit 10

Explanation: If executed at 2023-10-26 10:45:00 UTC, this returns events from 10:15:00 UTC to 10:45:00 UTC. This window captures events 103 through 107 (conceptual match based on sample data timing).

Output:

_TIME EVENT_ID EVENT_DESCRIPTION
2023-10-26 10:15:15 UTC 103 Network connection established
2023-10-26 10:20:00 UTC 104 System heartbeat
2023-10-26 10:30:45 UTC 105 Data transformation
2023-10-26 10:40:10 UTC 106 Unauthorized access detected
2023-10-26 10:45:00 UTC 107 Cloud resource modification

Example 5: Relative time (from beginning to past offset)

Goal: Retrieve records from the Unix epoch up to a specific relative point in the past.

XQL code:

config timeframe between "begin" and "-1y"
| dataset = sample_xql_raw
| fields _time, event_id, event_description
| limit 10

Explanation: This queries data from 1970-01-01 up to one year ago from the current execution time. If "now" is significantly in the future relative to the sample data (for example, late 2024), this would successfully retrieve the 2023 records.

Output:

_TIME EVENT_ID EVENT_DESCRIPTION
2023-10-26 10:00:00 UTC 101 User login successful
2023-10-26 10:05:30 UTC 102 File access attempt
... ... ...

Example 6: Relative time (between two offsets)

Goal: Define a relative window spanning between two points in the past relative to the current execution time.

XQL code:

config timeframe between "-2h" and "-1h"
| dataset = sample_xql_raw
| fields _time, event_id, event_description
| limit 10

Explanation: If executed at 2023-10-26 11:00:00 UTC, the window is 09:00:00 to 10:00:00. Only event 101 (at exactly 10:00:00) fits this window.

Output:

_TIME EVENT_ID EVENT_DESCRIPTION
2023-10-26 10:00:00 UTC 101 User login successful

Example 7: Exact timeframe

Goal: Define a precise, static start and end timestamp for the query.

XQL code:

config timeframe between "2023-10-26 10:00:00 UTC" and "2023-10-26 10:30:00 UTC"
| dataset = sample_xql_raw
| fields _time, event_id, event_description
| limit 100

Explanation: This query explicitly selects records between 10:00:00 and 10:30:00 UTC on the specified date.

Output:

_TIME EVENT_ID EVENT_DESCRIPTION
2023-10-26 10:00:00 UTC 101 User login successful
2023-10-26 10:05:30 UTC 102 File access attempt
2023-10-26 10:15:15 UTC 103 Network connection established
2023-10-26 10:20:00 UTC 104 System heartbeat
2023-10-26 10:30:45 UTC 105 Data transformation

dataset

Use the dataset stage to explicitly define the data table your XQL query will retrieve records from. This is a fundamental component used to specify the data source and is crucial for query optimization.

Syntax

dataset [=] <dataset_name>
dataset in (<dataset_name1>, <dataset_name2>, ...)
cold_dataset = <dataset_name>

Parameters

Name Type Required Description
dataset_name string Yes The name of the dataset to query. If omitted, the query defaults to xdr_data.
in operator No An operator used to specify a list of multiple datasets to query simultaneously.

Returns

The dataset stage returns raw records and all available fields from the specified data source(s).

Usage notes

  • The dataset stage must always be the very first stage in your XQL query, unless it is preceded by a config stage.
  • If you do not explicitly define a dataset, Cortex XDR/XSIAM will default to querying the xdr_data dataset.
  • Dataset names in queries are always treated as if they are lowercase, even if you type them with uppercase characters (Case Insensitivity).
  • Specifying the dataset early is a best practice for streamlining queries, as it helps reduce the amount of data processed.
  • The cold_dataset syntax is used specifically for querying data stored in cold storage.

Examples

Example 1: Basic Dataset Specification

Goal: Explicitly specify a single data source for the query.

XQL code:

config timeframe = 1d // Set a practical timeframe for the query
| dataset = sample_xql_raw // Specifies the data source for the query
| limit 5 // Limits the number of returned records to improve performance

Explanation: This query retrieves all available fields from the sample_xql_raw dataset within the specified timeframe.

Output:

event_id _time event_description
101 2023-10-26 10:00:00 UTC "User login successful"
102 2023-10-26 10:05:30 UTC "File access attempt"
103 2023-10-26 10:15:15 UTC "Network connection established"
104 2023-10-26 10:20:00 UTC "System heartbeat"
105 2023-10-26 10:30:45 UTC "Data transformation"

Example 2: Specifying Multiple Datasets

Goal: Query across several different datasets using the in operator.

XQL code:

config timeframe = 1d // Ensures the query operates within a defined time window
| dataset in ("sample_xql_raw") // Specifies one or more datasets to query from
| filter is_successful = true // Filters for successful events
| fields event_id, event_description, is_successful // Selects only the necessary fields for output
| limit 5 // Prevents excessive results and improves query performance

Explanation: This query demonstrates the dataset in syntax. While sample_xql_raw is a single dataset, this syntax allows listing multiple datasets (for example, dataset in ("dataset1", "dataset2")) to aggregate results from multiple sources.

Output:

event_id event_description is_successful
101 "User login successful" true
103 "Network connection established" true
104 "System heartbeat" true
105 "Data transformation" true
107 "Cloud resource modification" true

Example 3: cold_dataset Note (conceptual example)

Goal: Query data that is stored in cold storage.

XQL code:

config timeframe = 7d
| cold_dataset = sample_xql_raw_cold // Placeholder for a conceptual cold dataset
| limit 10

Explanation: The cold_dataset = <dataset name> syntax is used specifically for querying data stored in cold storage. This example conceptually queries a cold dataset named sample_xql_raw_cold.

Output:

event_id _time event_description
101 2023-10-26 10:00:00 UTC "User login successful"
102 2023-10-26 10:05:30 UTC "File access attempt"
103 2023-10-26 10:15:15 UTC "Network connection established"

Example 4: Default dataset (xdr_data)

Goal: Implicitly query the default dataset (xdr_data) by omitting the dataset stage.

XQL code:

// This query implicitly uses the 'xdr_data' dataset
filter event_id = 101
| fields event_id, event_description

Explanation: If no dataset stage is explicitly defined, XQL queries automatically run against the xdr_data dataset. This example directly filters and selects fields, implicitly targeting the default dataset (represented here by sample_xql_raw data for context).

Output:

event_id event_description
101 "User login successful"

dedup

Use the dedup stage to eliminate redundant records from your query's result set, ensuring that each returned record or combination of field values is unique.

Syntax

dedup <field1>[,<field2>, ...] by asc | desc <field>

Parameters

Name Type Required Description
field1, field2... string, integer, float Yes One or more fields used to identify duplicate records. If multiple fields are specified, the combination of values must be unique.
by clause No Determines which of the duplicate records is kept based on the value of a specified field. You must specify asc (ascending) or desc (descending).

Returns

The dedup stage returns a result set where the specified field (or combination of fields) contains only unique values.

Usage notes

  • When records contain duplicate values (or duplicate sets of values) for the specified fields, dedup ensures only one such record is retained.
  • The by clause is optional but crucial: it determines which of the duplicate records is kept. If no by clause is provided, the record returned is arbitrary among the duplicates.
  • The dedup stage can only be used with fields that contain numbers or strings.
  • Note that the dedup stage does not preserve the sort order established by preceding stages. If a specific order is required for the final output after deduplication, a sort stage should be placed after dedup.
  • While highly useful, the dedup stage can be resource-intensive, especially when applied to very large datasets like xdr_data. This is because dedup operations often involve a self-join, which can significantly impact query performance and cost.
  • Best practice: Avoid using dedup unless it is explicitly necessary for your analytical objective. When you do use it, ensure that any preceding filter or fields stages have already minimized the data being processed to optimize performance.

Examples

Example 1: Basic dedup on a single field

Goal: Remove duplicate records based on the specified single field (is_successful), retaining an arbitrary record from the duplicates.

XQL code:

dataset = sample_xql_raw
| fields event_id, is_successful 
| dedup is_successful

Explanation: This query removes duplicate records based on the is_successful field. Since no by clause is provided, the specific event_id associated with each true or false result is arbitrary.

Output:

EVENT_ID IS_SUCCESSFUL
101 true
102 false

Example 2: Dedup by earliest time

Goal: Return unique records based on a field, explicitly keeping the record with the earliest timestamp.

XQL code:

dataset = sample_xql_raw
| fields event_id, _time, is_successful
| dedup is_successful by asc _time

Explanation: This query deduplicates based on is_successful. By using by asc _time, it ensures that for any set of duplicate records (for example, all successful events), the record with the earliest _time value is kept.

Output:

EVENT_ID _TIME IS_SUCCESSFUL
101 2023-10-26 10:00:00 UTC true
102 2023-10-26 10:05:30 UTC false

Example 3: Dedup by latest time

Goal: Return unique records based on a field, explicitly keeping the record with the latest timestamp.

XQL code:

dataset = sample_xql_raw
| fields event_id, _time, is_successful
| dedup is_successful by desc _time

Explanation: This query deduplicates based on is_successful. By using by desc _time, it ensures that for any set of duplicate records, the record with the latest _time value is kept.

Output:

EVENT_ID _TIME IS_SUCCESSFUL
110 2023-10-26 11:00:10 UTC true
109 2023-10-26 10:55:55 UTC false

Example 4: Dedup on multiple fields

Goal: Remove records where the combination of values across multiple specified fields is identical.

XQL code:

dataset = sample_xql_raw
| fields event_id, event_description, is_successful
| dedup event_description, is_successful

Explanation: This query removes records where the combination of event_description and is_successful is duplicated. In the sample data, since each event_description is unique, the combinations are unique, and all records are returned. If duplicates existed, only one per combination would be kept.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL
101 "User login successful" true
102 "File access attempt" false
103 "Network connection established" true
104 "System heartbeat" true
105 "Data transformation" true
106 "Unauthorized access detected" false
107 "Cloud resource modification" true
108 "Software update initiated" true
109 "API request throttled" false
110 "Database backup completed" true

Example 5: Dedup on multiple fields with a by clause

Goal: Deduplicate based on multiple fields and use a specific ordering criterion to determine which record to keep.

XQL code:

dataset = sample_xql_raw
| fields event_id, event_description, is_successful, duration_seconds
| dedup event_description, is_successful by asc duration_seconds

Explanation: This query deduplicates based on the unique combination of event_description and is_successful. The by asc duration_seconds clause specifies that if duplicate combinations were found, the record with the smallest duration_seconds would be retained.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL DURATION_SECONDS
101 "User login successful" true 1.5
102 "File access attempt" false 0.8
103 "Network connection established" true 10.2
104 "System heartbeat" true 0.1
105 "Data transformation" true 5.0
106 "Unauthorized access detected" false 2.1
107 "Cloud resource modification" true 7.8
108 "Software update initiated" true 15.3
109 "API request throttled" false 0.05
110 "Database backup completed" true 60.0

fields

Use the fields stage to precisely define the columns that are returned in your XQL query result set. Use this stage, to ensure that all subsequent query stages operate exclusively on the columns you have explicitly declared.

Syntax

fields [-] <field_1> [as <alias_1>], <field_2> [as <alias_2>], ...

Parameters

Name Type Required Description
field_n string Yes The name of the field to include in the results.
alias_n string No The alias name to assign to the field using the as clause.
- operator No The minus character used to exclude a specific field from the result set.
as clause No The clause used to assign an alias to an existing field.

Returns

The fields stage returns specific columns, which are then utilized as fields in all subsequent stages of the query.

Usage notes

  • In Cortex Data Model (XDM) queries, the syntax is as follows: fields [-] fieldset.xdm_<fieldset name1>, fieldset.xdm_<fieldset name2>, ..., where the field names are replaced by dataset_name.field_name. For example, fields amazon_eks_raw.logStream.
  • In dataset queries, the following system fields cannot be excluded and are always displayed if they exist in the results: _time, _insert_time, _raw_log, _product, _vendor, _tag, _snapshot_id, _snapshot_log_count, _snapshot_collection_ts, and _id.
  • In XDM queries, the _time field cannot be excluded and is always displayed if it exists in the results.
  • The fields stage does not allow the use of any functions.
  • New fields and field values cannot be created within the fields stage; they must be created within the alter stage.
  • If you use the as clause, all subsequent stages in the query must refer to the field by its new alias.
  • To perform exclusion from a wider set of selected fields, you must use multiple fields stages: one for initial inclusion, followed by one or more for exclusion.
  • Employing the fields stage early in your query, immediately after any primary filtering, significantly reduces the amount of data processed.
  • Avoid using fields (or omitting the fields stage when running dataset queries) with large datasets like xdr_data, as this can vastly impact performance.

Examples

Example 1: Basic field selection

Goal: Explicitly list the field names you wish to include in your results.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, event_description
| limit 2

Explanation: The query selects only the event_id and event_description columns from the dataset.

Output:

event_id event_description
101 "User login successful"
102 "File access attempt"

Example 2: Aliasing fields

Goal: Rename fields for clarity or brevity in your result set using the as clause.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id as EventIdentifier, is_successful as Status
| limit 2

Explanation: The query renames event_id to EventIdentifier and is_successful to Status in the output.

Output:

EventIdentifier Status
101 true
102 false

Example 3: Including fields with wildcards

Goal: Include all fields that match a specified pattern using a wildcard (*).

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_* | limit 2

Explanation: The query selects all fields starting with the string "event_", such as event_id and event_description.

Output:

event_id event_description
101 "User login successful"
102 "File access attempt"

Example 4: Excluding fields

Goal: Exclude a specific field from the result set using the minus character (-).

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_*, _time, is_successful 
| fields - event_id 
| limit 2

Explanation: The query first selects a broad set of fields including event_*, _time, and is_successful. A second fields stage then explicitly excludes event_id from that selection.

Output:

_time event_description is_successful
2023-10-26 10:00:00 UTC "User login successful" true
2023-10-26 10:05:30 UTC "File access attempt" false

filter

Use the filter stage to precisely narrow down the data records returned by your query. The stage identifies which records should be included in the result set by evaluating boolean expressions. If a record satisfies the filter condition (i.e., the expression returns true), it is returned; otherwise, it is excluded.

Syntax

filter <boolean_expression>

Parameters

Name Type Required Description
boolean_expression boolean Yes A logical expression that evaluates to true or false. Only records where this expression evaluates to true are passed to the next stage.

Returns

The filter stage returns a subset of the original dataset containing only the records for which the boolean expression evaluates to true.

Usage notes

  • Applying the filter stage as early as possible in your query is a best practice. This significantly reduces the dataset size for subsequent stages, improving performance and reducing processing cost, especially with large datasets.
  • The filter stage supports various operators to express diverse filtering conditions, including comparison operators (=, !=, <, <=, >, >=), boolean operators (and, or, not), and string/range operators (in, not in, contains, ~=, !~=, incidr, incidr6).
  • Parentheses () can be used to explicitly define the order of evaluation for logical conditions.
  • Filters can check for the presence or absence of values using null or empty string "".
  • XQL treats string values differently based on whether single (") or triple (""") double quotes are used. Single double quotes treat the string literally (where * matches any sequence), while triple double quotes enable regex-style pattern matching and escape sequence interpretation.
  • You can employ subqueries within the filter stage, particularly with the in or not in operators, to define a list of values for comparison derived from another dataset or subset.
  • Functions can be used within filters (for example, to_number(), json_extract_scalar()) for complex data transformations and evaluations during the filtering process.

Examples

Example 1: Comparison operator - equals (=)

Goal: Filter for records where the event_id is exactly 101.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id = 101
| fields event_id, event_description

Explanation: Returns true if the field value is exactly equal to the specified value.

Output:

EVENT_ID EVENT_DESCRIPTION
101 "User login successful"

Example 2: Comparison operator - not equal to (!=)

Goal: Filter for records where the is_successful field is not true.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter is_successful != true
| fields event_id, is_successful

Explanation: Returns true if the field value is not equal to the specified value.

Output:

EVENT_ID IS_SUCCESSFUL
102 false
106 false
109 false

Example 3: Comparison operator -less than (<)

Goal: Filter for records where duration_seconds is strictly less than 1.0.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter duration_seconds < 1.0
| fields event_id, duration_seconds

Explanation: Returns true if the numeric field value is strictly less than the specified value.

Output:

EVENT_ID DURATION_SECONDS
104 0.1
109 0.05

Example 4: Comparison operator - less than or equal to (<=)

Goal: Filter for records where the extracted JSON status code is less than or equal to 200.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter to_number(json_extract_scalar(simple_json_data, "$.code")) <= 200
| fields event_id, simple_json_data

Explanation: Returns true if the numeric field value is less than or equal to the specified value.

Output:

EVENT_ID SIMPLE_JSON_DATA
101 {"status": "ok", "code": 200}
102 {"status": "fail", "error": "access_denied"}

Example 5: Comparison operator - greater than (>)

Goal: Filter for records where duration_seconds is strictly greater than 5.0.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter duration_seconds > 5.0
| fields event_id, duration_seconds

Explanation: Returns true if the numeric field value is strictly greater than the specified value.

Output:

EVENT_ID DURATION_SECONDS
103 10.2
107 7.8
108 15.3
110 60.0

Example 6: Comparison operator - greater than or equal to (>=)

Goal: Filter for records where the extracted JSON status code is greater than or equal to 200.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter to_number(json_extract_scalar(simple_json_data, "$.code")) >= 200
| fields event_id, simple_json_data

Explanation: Returns true if the numeric field value is greater than or equal to the specified value.

Output:

EVENT_ID SIMPLE_JSON_DATA
101 {"status": "ok", "code": 200}

Example 7: Boolean operator - AND (and)

Goal: Filter for records where is_successful is true AND duration_seconds is greater than 5.0.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter is_successful = true and duration_seconds > 5.0
| fields event_id, is_successful, duration_seconds

Explanation: Returns true only if all conditions separated by and are met.

Output:

EVENT_ID IS_SUCCESSFUL DURATION_SECONDS
103 true 10.2
107 true 7.8
108 true 15.3
110 true 60.0

Example 8: Boolean operator - OR (or)

Goal: Filter for records where event_description is "User login successful" OR is_successful is false.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_description = "User login successful" or is_successful = false
| fields event_id, event_description, is_successful

Explanation: Returns true if any of the conditions separated by or are met.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL
101 "User login successful" true
102 "File access attempt" false
106 "Unauthorized access detected" false
109 "API request throttled" false

Example 9: Boolean operator - NOT (not)

Goal: Filter for records where is_successful is NOT true.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter not is_successful = true
| fields event_id, is_successful

Explanation: Returns true if the condition immediately following not is not met.

Output:

EVENT_ID IS_SUCCESSFUL
102 false
106 false
109 false

Example 10: Boolean operator - parentheses grouping

Goal: Filter for records where the description is either "User login successful" or "System heartbeat", AND is_successful is true.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter (event_description = "User login successful" or event_description = "System heartbeat") and is_successful = true
| fields event_id, event_description, is_successful

Explanation: Parentheses explicitly define the order of evaluation for logical conditions.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL
101 "User login successful" true
104 "System heartbeat" true

Example 11: String operator - IN (in)

Goal: Filter for records where event_description matches any value in the provided list.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_description in ("User login successful", "Network connection established")
| fields event_id, event_description

Explanation: Checks if a field's value is present within a provided list.

Output:

EVENT_ID EVENT_DESCRIPTION
101 "User login successful"
103 "Network connection established"

Example 12: String operator - NOT IN (not in)

Goal: Filter for records where event_description does NOT match any pattern in the provided list.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_description not in ("*heartbeat*", "*transformation*")
| fields event_id, event_description

Explanation: Checks if a field's value is not present within a provided list. Supports wildcards (*) for string values.

Output:

EVENT_ID EVENT_DESCRIPTION
101 "User login successful"
102 "File access attempt"
103 "Network connection established"
106 "Unauthorized access detected"
107 "Cloud resource modification"
108 "Software update initiated"
109 "API request throttled"
110 "Database backup completed"

Example 13: String operator - contains

Goal: Filter for records where raw_log_data contains the substring "Process".

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter raw_log_data contains "Process"
| fields event_id, raw_log_data

Explanation: Returns true if the string field value contains the specified substring.

Output:

EVENT_ID RAW_LOG_DATA
102 "Process cmd.exe attempted to access /etc/passwd"

Example 14: Regular expression match (~=)

Goal: Filter for records where event_description matches a regex pattern for login success or failure.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_description ~= ".*login (?:successful|failed).*"
| fields event_id, event_description

Explanation: Returns true if the regular expression matches.

Output:

EVENT_ID EVENT_DESCRIPTION
101 "User login successful"
106 "Unauthorized access detected"

Example 15: Regular expression does not match (!~=)

Goal: Filter for records where event_description does NOT match a case-insensitive regex pattern for system heartbeat or update.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter not event_description ~= "(?i).*system (?:heartbeat|update).*"
| fields event_id, event_description

Explanation: Returns true if the regular expression does not match.

Output:

EVENT_ID EVENT_DESCRIPTION
101 "User login successful"
102 "File access attempt"
103 "Network connection established"
105 "Data transformation"
106 "Unauthorized access detected"
109 "API request throttled"
110 "Database backup completed"

Example 16: Range operator - incidr (IPv4)

Goal: Filter for records where ipv4_address falls within the 192.168.1.0/24 CIDR range.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter ipv4_address incidr "192.168.1.0/24"
| fields event_id, ipv4_address

Explanation: Returns true if an IPv4 address falls within specified CIDR ranges.

Output:

EVENT_ID IPV4_ADDRESS
101 "192.168.1.10"

Example 17: Range operator - not incidr (IPv4)

Goal: Filter for records where ipv4_address does NOT fall within typical private IP ranges.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter ipv4_address not incidr "10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16"
| fields event_id, ipv4_address

Explanation: Returns true if an IPv4 address does not fall within the specified CIDR ranges.

Output:

EVENT_ID IPV4_ADDRESS
106 "203.0.113.15"

Example 18: Range operator - incidr6 (IPv6)

Goal: Filter for records where ipv6_address falls within the 2001:0db8::/32 CIDR range.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter ipv6_address incidr6 "2001:0db8::/32"
| fields event_id, ipv6_address

Explanation: Returns true if an IPv6 address falls within specified CIDR6 ranges.

Output:

EVENT_ID IPV6_ADDRESS
103 "2001:0db8::1"
107 "2001:0db8:cafe::1"
109 "2001:0db8:1234::abcd"

Example 19: Range operator - not incidr6 (IPv6)

Goal: Filter for records where ipv6_address does NOT fall within the fe80::/10 CIDR range.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter ipv6_address not incidr6 "fe80::/10"
| fields event_id, ipv6_address

Explanation: Returns true if an IPv6 address does not fall within the specified CIDR6 range.

Output:

EVENT_ID IPV6_ADDRESS
103 "2001:0db8::1"
107 "2001:0db8:cafe::1"
109 "2001:0db8:1234::abcd"

Example 20: Null value filtering (AND)

Goal: Filter for records where ipv6_address is neither null nor empty.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter ipv6_address != null and ipv6_address != ""
| fields event_id, ipv6_address

Explanation: Filters out records where a field is null or an empty string "".

Output:

EVENT_ID IPV6_ADDRESS
103 "2001:0db8::1"
107 "2001:0db8:cafe::1"
109 "2001:0db8:1234::abcd"

Example 21: Null value filtering (NOT IN)

Goal: Filter for records where ipv6_address is not in the set of null or empty strings.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter ipv6_address not in("", null)
| fields event_id, ipv6_address

Explanation: An alternative way to filter out nulls or empty strings using the not in operator.

Output:

EVENT_ID IPV6_ADDRESS
103 "2001:0db8::1"
107 "2001:0db8:cafe::1"
109 "2001:0db8:1234::abcd"

Example 22: String manipulation - single quotes

Goal: Filter using single double quotes ("text") where wildcards behave normally.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_description = "User login*"
| fields event_id, event_description

Explanation: Treats the string value literally. Wildcards (*) match any sequence of characters. Escape sequences are treated as plain characters.

Output:

EVENT_ID EVENT_DESCRIPTION
101 "User login successful"

Example 23: String manipulation - triple quotes

Goal: Filter using triple double quotes ("""text""") to enable regex-style pattern matching.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter raw_log_data = """Process cmd.exe*passwd"""
| fields event_id, raw_log_data

Explanation: Enables regex-style pattern matching and escape sequence interpretation. Wildcards (*) still act as XQL wildcards.

Output:

EVENT_ID RAW_LOG_DATA
102 "Process cmd.exe attempted to access /etc/passwd"

Example 24: Subquery filtering - IN

Goal: Filter for events in the main dataset whose IDs are found in a specific subset of successful events (using a subquery).

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id in (
    dataset = sample_xql_raw
    | filter is_successful = true and duration_seconds > 5.0
    | fields event_id
)
| fields event_id, event_description, is_successful, duration_seconds

Explanation: The inner query identifies event_ids where is_successful is true AND duration_seconds > 5.0. The outer filter then includes only records from the main dataset that have these event_ids.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL DURATION_SECONDS
103 "Network connection established" true 10.2
107 "Cloud resource modification" true 7.8
108 "Software update initiated" true 15.3
110 "Database backup completed" true 60.0

Example 25: Subquery filtering - NOT IN

Goal: Exclude events whose IDs are found in a specific subset of unsuccessful events (using a subquery).

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id not in (
    dataset = sample_xql_raw
    | filter is_successful = false and duration_seconds < 1.0
    | fields event_id
)
| fields event_id, event_description, is_successful, duration_seconds

Explanation: The inner query identifies event_ids where is_successful is false AND duration_seconds < 1.0. The outer filter includes all records except those with these specific event_ids (102 and 109).

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL DURATION_SECONDS
101 "User login successful" true 1.5
103 "Network connection established" true 10.2
104 "System heartbeat" true 0.1
105 "Data transformation" true 5.0
106 "Unauthorized access detected" false 2.1
107 "Cloud resource modification" true 7.8
108 "Software update initiated" true 15.3
110 "Database backup completed" true 60.0

iploc

Use the iploc stage to enrich event data by associating IPv4 addresses with predefined geolocation attributes. This capability is fundamental for security analysts to understand the geographic origin or destination of network activity within their environment.

Syntax

iploc <field> [loc_field1 [as <alias1>], loc_field2 [as <alias2>], ...]

Parameters

Name Type Required Description
field string, integer Yes The input field containing the IPv4 address to be geolocated.
loc_field string No Specific geolocation attributes to return (for example, loc_country, loc_city). If omitted, a default set is added.
alias string No The alias name for the geolocation field, using the as clause.

Returns

The iploc stage returns the original event records enriched with additional columns containing geolocation information (such as loc_country, loc_city, loc_latlon, etc.) corresponding to the provided IPv4 address.

Usage notes

  • The iploc stage can only be used with fields that contain numbers or strings.
  • Geolocation data generated by this stage can be visualized on a graph of type = map, where the x-axis is set to loc_country or loc_latlon, and the y-axis is a number field.
  • Optimization: It is crucial to optimize queries for performance and cost. Apply filter stages before iploc to reduce the dataset size, ensuring iploc only processes relevant records.
  • Optimization: Use the fields stage early in your query to select only the necessary columns, minimizing the data processed by subsequent stages including iploc.

Examples

Example 1: Basic iploc (default geolocation fields)

Goal: Enrich the ipv4_address field with the default set of geolocation attributes.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 5
| iploc ipv4_address

Explanation: The query applies the iploc stage to the ipv4_address field. Since no specific output fields are defined, it returns the default geolocation columns. Note that for private IPs (RFC1918), the country is often identified as "Private" with NULL values for granular details.

Output:

EVENT_ID IPV4_ADDRESS LOC_COUNTRY LOC_CITY LOC_LATLON
101 192.168.1.10 Private NULL NULL
102 10.0.0.5 Private NULL NULL
103 NULL NULL NULL NULL
104 172.31.255.255 Private NULL NULL
105 192.168.10.20 Private NULL NULL

Example 2: Specifying selected geolocation fields

Goal: Enrich the ipv4_address field but return only the Country and City attributes.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 5
| iploc ipv4_address loc_country, loc_city

Explanation: The query uses iploc on ipv4_address but explicitly requests only the loc_country and loc_city columns to be added to the result set.

Output:

EVENT_ID IPV4_ADDRESS LOC_COUNTRY LOC_CITY
101 192.168.1.10 Private NULL
102 10.0.0.5 Private NULL
103 NULL NULL NULL
104 172.31.255.255 Private NULL
105 192.168.10.20 Private NULL

Example 3: Aliasing geolocation fields

Goal: Enrich the ipv4_address field with specific attributes and rename the output columns for clarity.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| limit 5
| iploc ipv4_address loc_country as IP_Country, loc_latlon as IP_LatLon

Explanation: The query extracts loc_country and loc_latlon for the ipv4_address, renaming them to IP_Country and IP_LatLon respectively using the as clause.

Output:

EVENT_ID IPV4_ADDRESS IP_COUNTRY IP_LATLON
101 192.168.1.10 Private NULL
102 10.0.0.5 Private NULL
103 NULL NULL NULL
104 172.31.255.255 Private NULL
105 192.168.10.20 Private NULL

Example 4: Viewing iploc results as a map graph

Goal: Combine iploc with aggregation to count IP addresses by country and visualize the data.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| iploc ipv4_address loc_country as country
| filter country != null and ipv4_address != null
| comp count() as ip_count by country
| view graph type = map xaxis = country yaxis = ip_count
| limit 10

Explanation: The query enriches the data with loc_country (aliased as country), filters out nulls, and aggregates the count of IPs per country. The view stage then configures the output to be displayed as a map graph. The table output below represents the underlying data for the visualization.

Output:

COUNTRY IP_COUNT
Private 8
NULL 2

join

Use the join stage to combine the results of two queries into a single result set based on a specified condition.

Syntax

join conflict_strategy = both|left|right type = inner|left|right ((<xql query>) as <execution_name> <boolean_expr>)

Parameters

Name Type Required Description
conflict_strategy string No Identifies how column name conflicts are resolved if a field name exists in both the parent (left) query's result set and the joined (right) query's result set. Valid values are both, left, or right. The default is right.
type string No Defines the type of join, dictating which records are included in the final result set based on the join condition. Valid values are inner, left, or right. The default is inner.
xql query query block Yes The XQL query whose results you want to combine with the parent query. This sub-query must be enclosed in parentheses.
execution_name string Yes Provides an alias for the joined query's result set using the as clause. This alias is used to refer to fields from the joined query (for example, alias.field_name).
boolean_expr expression Yes Identifies the conditions (join keys) that must be met to place a record in the join result set.

Returns

The join stage returns a unified result set containing rows from the parent query combined with rows from the sub-query, based on the specified join type and condition.

Usage notes

  • The join stage can combine results, but it does not preserve sort order. If sorting is needed, specify the sort stage after the join stage.
  • join operations, especially on large datasets like xdr_data, can be resource-intensive. It is a Best practice to pare down datasets before joining them.
  • Always use the fields stage early in your queries, including within the sub-queries of a join, to select only necessary columns. This minimizes the data processed and improves performance.
  • The config case_sensitive stage can be used at the beginning of the query or when adding a join stage to control case sensitivity for field value evaluation.
  • By default, forensic datasets are not included in XQL query results unless explicitly defined. Queries for forensic data require specific enabling steps.

Examples

Example 1: Inner join

Goal: Perform an inner join to return only the records where there is a match in event_id from both the left and right datasets.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id in (101, 102, 103, 104) // Simulates left side
| fields event_id, event_description
| join type = inner (
    dataset = sample_xql_raw
    | filter event_id in (103, 104, 105, 106) // Simulates right side
    | fields event_id as joined_event_id, is_successful as success_status
) as right_data right_data.joined_event_id = event_id // Join condition

Explanation: This query joins records from sample_xql_raw where event_id is 101, 102, 103, or 104, with records where event_id is 103, 104, 105, or 106. Only event_ids present in both sets will be returned. The joined_event_id from right_data is matched with event_id from the main query.

Output:

event_id event_description joined_event_id success_status
103 Network connection established 103 true
104 System heartbeat 104 true

Example 2: Left join

Goal: Perform a left join to return all records from the left dataset, and the matching records from the right.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id in (101, 102, 103, 104) // Simulates left side
| fields event_id, event_description
| join type = left (
    dataset = sample_xql_raw
    | filter event_id in (103, 104, 105, 106) // Simulates right side
    | fields event_id as joined_event_id, is_successful as success_status
) as right_data right_data.joined_event_id = event_id // Join condition

Explanation: This query returns all event_ids 101, 102, 103, 104 from the left side. For event_ids 101 and 102, since there are no matching records on the right side, NULL is populated for joined_event_id and success_status.

Output:

event_id event_description joined_event_id success_status
101 User login successful NULL NULL
102 File access attempt NULL NULL
103 Network connection established 103 true
104 System heartbeat 104 true

Example 3: Right join

Goal: Perform a right join to return all records from the right dataset, and the matching records from the left.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id in (101, 102, 103, 104) // Simulates left side
| fields event_id, event_description
| join type = right (
    dataset = sample_xql_raw
    | filter event_id in (103, 104, 105, 106) // Simulates right side
    | fields event_id as joined_event_id, is_successful as success_status
) as right_data right_data.joined_event_id = event_id // Join condition

Explanation: This query returns all event_ids 103, 104, 105, 106 from the right side. For event_ids 105 and 106, since there are no matching records on the left side, NULL is populated for event_description.

Output:

event_id event_description joined_event_id success_status
103 Network connection established 103 true
104 System heartbeat 104 true
NULL NULL 105 true
NULL NULL 106 false

Example 4: Conflict strategy left

Goal: Perform an inner join while prioritizing the column from the original (parent) query's result set when a conflict exists.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id in (101, 102, 103, 104) // Simulates left side with original event_description
| fields event_id, event_description
| join conflict_strategy = left // Prioritize left side event_description
type = inner (
    dataset = sample_xql_raw
    | filter event_id in (103, 104, 105, 106) // Simulates right side data for join
    | fields event_id as joined_event_id, is_successful as event_description // This event_description will be discarded due to conflict_strategy = left
) as right_data right_data.joined_event_id = event_id // Join condition
| fields event_id, event_description, joined_event_id // Select fields to show output

Explanation: The sub-query renames is_successful to event_description to create a conflict. Since conflict_strategy = left is specified, the event_description from the primary (left) query, which contains string values (for example, "Network connection established"), is preserved.

Output:

event_id event_description joined_event_id
103 Network connection established 103
104 System heartbeat 104

Example 5: Conflict strategy right

Goal: Perform an inner join while prioritizing the column from the inner (joined) query's result set when a conflict exists.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter event_id in (101, 102, 103, 104) // Simulates left side with original event_description
| fields event_id, event_description
| join conflict_strategy = right // Prioritize right side event_description
type = inner (
    dataset = sample_xql_raw
    | filter event_id in (103, 104, 105, 106) // Simulates right side data for join
    | fields event_id as joined_event_id, is_successful as event_description // This event_description will be kept
) as right_data right_data.joined_event_id = event_id // Join condition
| fields event_id, event_description, joined_event_id // Select fields to show output

Explanation: With conflict_strategy = right, the event_description column from the inner (right) query is kept. This results in the event_description column containing the boolean values from is_successful (for example, true), while the original string event_description from the left query is discarded for the joined records.

Output:

event_id event_description joined_event_id
103 true 103
104 true 104

limit

Use the limit stage to explicitly set the upper bound for the number of records returned by an XQL query. This is crucial for optimizing query performance, reducing data processing volume, and minimizing memory usage for operations like sorting.

Syntax

limit <number>

Parameters

Name Type Required Description
<number> integer Yes The maximum number of records to return in the result set.

Returns

The limit stage returns a subset of the input records, restricted to the maximum count specified by the <number> parameter.

Usage notes

  • Unless a limit stage is explicitly stated, standard XQL queries have a default maximum limit of 1,000,000 results.
  • Basic XQL queries (and XDM queries in Cortex XSIAM) that contain no stages beyond a fields stage have a default limit of 1,000 results.
  • The 1,000-result default limit for basic queries does not apply to widgets, Correlation Rules, public APIs, saved queries, or scheduled queries, which maintain the 1,000,000-result limit if unspecified.
  • We recommend placing the limit stage after sorting (sort) to ensure you are retrieving the top or bottom records based on your criteria, rather than an arbitrary subset.
  • Applying limit after filtering (filter) ensures that the limit applies only to the relevant records, optimizing data processing.

Examples

Example 1: Basic limit to restrict total records

Goal: Retrieve a specified number of records from the dataset without specific ordering or filtering.

XQL code:

dataset = sample_xql_raw
| limit 5

Explanation: This query returns the first 5 records found in the sample_xql_raw dataset. Since no sort order is defined, the records are returned in the order they appear in the source.

Output:

event_id _time event_description is_successful duration_seconds simple_json_data nested_json_data array_of_json_objects string_tags numeric_codes raw_log_data ipv4_address ipv6_address
101 2023-10-26 10:00:00 UTC "User login successful" true 1.5 {"status": "ok", "code": 200} {"user": {"id": "U1", "name": "Alice"}, "session": {"start": "10:00", "type": "web"}} [{"action": "read", "file": "doc1.txt"}, {"action": "write", "file": "report.log"}] ["security", "login"] [13, -47, 29, 82, -15] "User Alice logged in from 192.168.1.10" "192.168.1.10" NULL
102 2023-10-26 10:05:30 UTC "File access attempt" false 0.8 {"status": "fail", "error": "access_denied"} {"process": {"name": "cmd.exe", "pid": 1234}, "target": {"path": "/var/log", "permission": "rwx"}} [{"event": "file_open", "path": "/etc/passwd"}] ["filesystem", "critical"] [-21, 56, 13, -88, 42] "Process cmd.exe attempted to access /etc/passwd" "10.0.0.5" NULL
103 2023-10-26 10:15:15 UTC "Network connection established" true 10.2 {"connection_id": "CONN-001", "protocol": "TCP"} {"source": {"ip": "172.16.0.1", "port": 5000}, "destination": {"ip": "1.1.1.1", "port": 443}} [{"conn_type": "outbound", "bytes": 1024}, {"conn_type": "inbound", "bytes": 512}] ["network", "cloud"] [90, -33, 7, 51, -62, 18] "Outbound connection to 1.1.1.1:443 initiated by AppX" NULL "2001:0db8::1"
104 2023-10-26 10:20:00 UTC "System heartbeat" true 0.1 {"health": "good"} {"system": {"cpu_util": 0.15, "mem_free": "80%"}, "status": "active"} [] ["monitoring"] [] "System health check passed" "172.31.255.255" NULL
105 2023-10-26 10:30:45 UTC "Data transformation" true 5.0 {"transform_stage": 1} {"pipeline": {"id": "P5", "status": "running"}, "data": {"records_in": 1000, "records_out": 950}} [{"step": "parse", "time_ms": 100}, {"step": "filter", "time_ms": 200}, {"step": "enrich", "time_ms": 300}] ["data_ops"] [77, -9, 35, -47, 61] "Transformed data from source X, processed 1000 records." "192.168.10.20" NULL

Example 2: Limit after a filter stage

Goal: Restrict the number of results returned after applying specific criteria to the dataset.

XQL code:

dataset = sample_xql_raw
| filter is_successful = false
| limit 2

Explanation: The query first filters for events where is_successful is false. The limit stage then restricts the output to the first 2 of these filtered records.

Output:

event_id _time event_description is_successful duration_seconds simple_json_data nested_json_data array_of_json_objects string_tags numeric_codes raw_log_data ipv4_address ipv6_address
102 2023-10-26 10:05:30 UTC "File access attempt" false 0.8 {"status": "fail", "error": "access_denied"} {"process": {"name": "cmd.exe", "pid": 1234}, "target": {"path": "/var/log", "permission": "rwx"}} [{"event": "file_open", "path": "/etc/passwd"}] ["filesystem", "critical"] [-21, 56, 13, -88, 42] "Process cmd.exe attempted to access /etc/passwd" "10.0.0.5" NULL
106 2023-10-26 10:40:10 UTC "Unauthorized access detected" false 2.1 {"alert_id": "SEC-001", "severity": "high"} {"actor": {"type": "user", "name": "unknown"}, "target": {"resource": "db_server", "action": "read"}} [{"alert_type": "login_fail", "count": 5}, {"alert_source": "IDS"}] ["security", "attack"] [-12, 24, 68, -59, 37, 80] "Multiple failed login attempts to db_server from external source." "203.0.113.15" NULL

Example 3: Limit after a sort stage

Goal: Retrieve a specific number of top or bottom records based on a field's value.

XQL code:

dataset = sample_xql_raw
| sort desc duration_seconds
| limit 3

Explanation: The query sorts all records by duration_seconds in descending order. The limit stage then returns the top 3 records, effectively showing the three events with the longest duration.

Output:

event_id _time event_description is_successful duration_seconds simple_json_data nested_json_data array_of_json_objects string_tags numeric_codes raw_log_data ipv4_address ipv6_address
110 2023-10-26 11:00:10 UTC "Database backup completed" true 60.0 {"backup_id": "DB-005", "size_gb": 500} {"db": {"name": "prod_db", "type": "SQL"}, "storage": {"location": "S3", "cost_usd": 15}} [{"stage": "compress", "time_s": 120}, {"stage": "upload", "time_s": 480}] ["database", "backup", "successful"] [27, -70, 92, 11, -36, 64] "Full backup of prod_db to S3 completed." "192.168.50.5" NULL
108 2023-10-26 10:50:20 UTC "Software update initiated" true 15.3 {"update_id": "SW-789", "status": "pending"} {"system": {"hostname": "webserver01", "os": "Linux"}, "patch": {"version": "1.2.3"}} [] ["maintenance", "system"] [38, -25, 73, 19, -81] "Patch deployment started on webserver01. Expected downtime: 15 min." "172.20.1.100" NULL
103 2023-10-26 10:15:15 UTC "Network connection established" true 10.2 {"connection_id": "CONN-001", "protocol": "TCP"} {"source": {"ip": "172.16.0.1", "port": 5000}, "destination": {"ip": "1.1.1.1", "port": 443}} [{"conn_type": "outbound", "bytes": 1024}, {"conn_type": "inbound", "bytes": 512}] ["network", "cloud"] [90, -33, 7, 51, -62, 18] "Outbound connection to 1.1.1.1:443 initiated by AppX" NULL "2001:0db8::1"

Example 4: Limit after comp (aggregation)

Goal: Restrict the number of aggregated groups returned in the result set.

XQL code:

dataset = sample_xql_raw
| filter is_successful = true
| comp count(event_id) as successful_events by string_tags
| limit 2

Explanation: The query filters for successful events and then counts them, grouping by their string_tags. The limit stage restricts the output to just 2 of these aggregated groups.

Output:

string_tags successful_events
["security", "login"] 1
["network", "cloud"] 1

pivot

Use the pivot stage to rotate row-level data into columns, aligning with BigQuery's native PIVOT operator. The Pivot stage applies one or more aggregation functions to a specified field and transposes the distinct values of a pivot column (specified in the FOR / IN clause) into new output columns. This is useful for transforming long-format data into a wide-format summary, making it easier to compare values side by side.

The pivot stage transforms row-oriented data into a columnar summary by:

  1. Selecting a column whose distinct values become new column headers (the pivot key, specified in the IN clause).
  2. Applying one or more aggregate functions to compute the values that populate those new columns.
  3. Optionally grouping the remaining rows by a by clause to produce one output column per group.

Syntax

pivot <aggregation_function>(<field>)[, <aggregation_function2>(<field2>), ...] for <pivot_column> IN ("<value1>", "<value2>", ...) [by <group_field1>[, <group_field2>, ...]]

Parameters

Name Type Required Description
aggregation_function function Yes The aggregation function to apply to the target field. Allowed functions: count, sum, avg, min, max, count_distinct, approx_count, var, stddev_sample, stddev_population, approx_quantiles, list. A maximum of 3 aggregation functions can be used within a single pivot stage. The allowed aggregation functions are subject to tenant configuration.
field string Yes The name of the field whose values are aggregated.
pivot_column string Yes The field (specified after for) whose distinct values become the new column names in the output. Must resolve to a primitive data type — JSON fields and Records cannot be used as the pivot key (see Limitations).
value1, value2, ... string Yes A comma-separated list of quoted values from the pivot_column that define which new columns to create. Each value becomes a separate column in the result. A maximum of 10 values can be specified in the IN clause.
group_field string No One or more fields specified after by that group the output rows. When the by clause is used, the total resulting group columns are capped to 5 values. If omitted, the output consists only of the aggregated data across the IN values.

Returns

The pivot stage returns a transformed dataset where:

  • The pivot_column is removed from the output schema.
  • A new column is created for each value specified in the IN (...) clause, named after that value.
  • Each new column contains the result of the aggregation function applied to the field for the corresponding pivot_column value.
  • When a single aggregation function is used, the new columns are named directly after the IN values (for example, NETWORK, FILE, PROCESS).
  • When multiple aggregation functions are used, the new columns follow the naming pattern <function>_<index>_<value> (for example, sum_1_NETWORK, max_2_FILE).
  • If the by clause is specified, the group fields are preserved in the output. If omitted, only the aggregated pivot columns are returned.
  • All other fields from the input dataset that are not part of the aggregation or pivot key are discarded.

Usage notes

  • The pivot stage is a "blocking" operation; it must process all input records before producing any output.
  • The values listed in the IN (...) clause must be quoted strings.
  • Only one IN condition with a single set of values is supported per pivot stage.
  • The pivot stage is not supported in Materialized Views.
  • The pivot stage is typically used after a data source stage (such as dataset) and can be combined with other stages like filter, alter, sort, comp, or fields for further processing.
  • After a pivot, the newly created columns (from the IN clause values) become regular columns in the dataset. They can be referenced in any subsequent stage — filter, alter, sort, fields, etc. — just like any other column.

Limitations

The following constraints apply to the pivot stage.

Feature Constraint / Behavior
Max Aggregations 3 functions per pivot stage. When multiple aggregations are used, column names are auto-generated (for example, sum_1_NETWORK).
Max Pivot Values 10 values in the IN clause. This threshold prevents "wide table" performance degradation and avoids exceeding BigQuery column limits.
Optional BY If the by clause is omitted, the output returns aggregate data only. When used, the by clause is limited to 5 group values to ensure query stability.
Data Types Primitive types only for the pivot key. The FOR expression must resolve to a primitive data type. JSON fields and Records cannot be used as the pivot key — they must be cast to STRING in a previous stage as a workaround.

Note: Not all XQL aggregation functions are supported in pivot. Functions such as first, last, least, and median are not supported. See the Parameters section for the full list of allowed aggregation functions.

Supported contexts

Context Supported
Correlation No
Parsing Rule No
Dataset RBAC No
Scheduled Queries Yes
Widgets/Reports Yes

Examples

Example 1: Basic Pivot — Single aggregation using the BY clause

Goal: Sum bytes transferred per host, pivoted across event types.

XQL code:

dataset = xdr_data
| pivot sum(bytes_transferred) for event_type IN ("NETWORK", "FILE", "PROCESS") by host
| limit 100

Explanation: This query takes the bytes_transferred field, applies a sum aggregation, and pivots the results so that each distinct event_type value (NETWORK, FILE, PROCESS) becomes its own column. The by host clause groups the output by host.

Starting dataset (xdr_data):

_id host event_type bytes_transferred
1 STORY NETWORK 1024
2 STORY NETWORK 2048
3 STORY FILE 512
4 STORY PROCESS 256
5 EVENT_LOG NETWORK 4096
6 EVENT_LOG FILE 768
7 EVENT_LOG PROCESS 128
8 INJECTION NETWORK 2048
9 INJECTION PROCESS 64

Output:

host NETWORK FILE PROCESS
STORY 3072 512 256
EVENT_LOG 4096 768 128
INJECTION 2048 null 64

Example 2: Pivot without a BY clause

Goal: Count events by type without grouping by any other field.

XQL code:

dataset = xdr_data
| pivot count(event_type) for event_type IN ("NETWORK", "FILE", "PROCESS", "REGISTRY")
| limit 100

Explanation: When the by clause is omitted, the output contains only the aggregated data across the IN values — a single row with one column per pivot value.

Before pivot:

event_type count(event_type)
NETWORK 5420
FILE 1893
PROCESS 3102
REGISTRY 764

Output:

NETWORK FILE PROCESS REGISTRY
5420 1893 3102 764

Example 3: Using pivoted columns in subsequent stages

Goal: Pivot event counts by host, then filter and sort the results.

XQL code:

dataset = xdr_data
| pivot count(event_type) for event_type IN ("NETWORK", "FILE", "PROCESS") by agent_hostname
| filter NETWORK > 100
| sort desc NETWORK
| fields agent_hostname, NETWORK, FILE, PROCESS
| limit 50

Explanation: After the pivot stage, the newly created columns (NETWORK, FILE, PROCESS) become regular fields in the dataset. They can be referenced in any subsequent stage — filter, sort, fields, etc. — just like any other column. Here, the query filters for hosts with more than 100 network events and sorts by the NETWORK column in descending order.

After pivot:

agent_hostname NETWORK FILE PROCESS
host-alpha 250 80 410
host-beta 150 42 310
host-gamma 88 17 205
host-delta 12 5 55

After filter (final output):

agent_hostname NETWORK FILE PROCESS
host-alpha 250 80 410
host-beta 150 42 310

Example 4: Multiple aggregations in a single pivot

Goal: Use two aggregate functions — sum and max — in one pivot statement.

XQL code:

dataset = xdr_data
| pivot sum(bytes_transferred), max(bytes_transferred) for event_type IN ("NETWORK", "FILE") by host
| limit 100

Explanation: This query applies both sum and max aggregations to bytes_transferred, pivoted by event_type. When multiple aggregation functions are used, the output column names follow the pattern <function>_<index>_<value> (for example, sum_1_NETWORK, max_2_FILE).

Starting dataset (xdr_data):

_id host event_type bytes_transferred
1 host-alpha NETWORK 1024
2 host-alpha NETWORK 3072
3 host-alpha FILE 512
4 host-alpha FILE 768
5 host-beta NETWORK 2048
6 host-beta FILE 256

Output:

host sum_1_NETWORK sum_2_FILE max_1_NETWORK max_2_FILE
host-alpha 4096 1280 3072 768
host-beta 2048 256 2048 256

Example 5: Pivot with two BY fields

Goal: Group the pivoted output by two fields to produce a more granular breakdown.

XQL code:

dataset = xdr_data
| pivot sum(bytes_transferred) for event_type IN ("NETWORK", "FILE") by host, source_zone
| limit 100

Explanation: This query pivots bytes_transferred by event_type and groups the results by both host and source_zone, producing a more detailed breakdown.

Starting dataset (xdr_data):

_id host source_zone event_type bytes_transferred
1 host-alpha DMZ NETWORK 1024
2 host-alpha DMZ NETWORK 512
3 host-alpha Internal NETWORK 2048
4 host-alpha Internal FILE 768
5 host-beta DMZ NETWORK 4096
6 host-beta DMZ FILE 256
7 host-beta Internal NETWORK 1536

Output:

host source_zone NETWORK FILE
host-alpha DMZ 1536 null
host-alpha Internal 2048 768
host-beta DMZ 4096 256
host-beta Internal 1536 null
  • Stages: comp, fields, filter, transpose
  • Functions: count, sum, min, max

preset

Use the preset stage to specify a predefined collection of fields from the xdr_data dataset as your data source, optimized for analyzing specific types of network or endpoint activity.

Syntax

preset = <preset_name>

Parameters

Name Type Required Description
preset_name string Yes The specific name of the preset to query (for example, authentication_story, network_story).

Returns

The preset stage returns a result set containing events and fields specific to the defined preset schema. These presets are often subsets of the xdr_data dataset, pre-filtered and organized for specific use cases like authentication, network traffic, or file operations.

Usage notes

  • Presets act as predefined views or "stories" (for example, authentication_story, network_story) that combine events and logs into a common schema.
  • While presets offer convenience by pre-grouping fields, XQL Query Optimization Principles advise that for specific use cases, directly querying dataset=xdr_data with explicit filters (for example, filter event_type = ...) can often lead to better performance.
  • General presets are designed for "well decorated, de-duplicated" results and may not prioritize performance impact; overly broad queries using presets can exceed optimization limits.
  • To optimize performance when using presets (or their raw dataset alternatives), always apply filter stages as early as possible and use the fields stage to select only the necessary columns.

Examples

Example 1: Authentication story preset

Goal: Analyze authentication events using the authentication_story preset.

XQL code:

config timeframe = 7d
| preset = authentication_story
| fields agent_hostname, os_actor_effective_username, action_local_ip
| limit 10

Explanation: This query uses the authentication_story preset to focus on authentication logs, stitching together relevant information into a common schema. An optimized alternative would be querying dataset = xdr_data and filtering by dfe_labels = "authentication".

Output:

AGENT_HOSTNAME OS_ACTOR_EFFECTIVE_USERNAME ACTION_LOCAL_IP
WORKSTATION-01 jdoe 192.168.1.15
SERVER-DB-02 admin 10.0.0.50
WORKSTATION-03 asmith 192.168.1.20

Example 2: Network story preset

Goal: Aggregate network activity information using the network_story preset.

XQL code:

config timeframe = 7d
| preset = network_story
| filter action_remote_port = 443
| fields agent_hostname, action_local_ip, action_remote_ip, action_remote_port
| limit 10

Explanation: This query utilizes the network_story preset to provide a common schema for network events and filters specifically for HTTPS traffic (port 443). An optimized alternative involves querying dataset = xdr_data with filter event_type = NETWORK.

Output:

AGENT_HOSTNAME ACTION_LOCAL_IP ACTION_REMOTE_IP ACTION_REMOTE_PORT
HOST-A 10.10.10.5 172.217.16.14 443
HOST-B 10.10.10.8 142.250.185.78 443
HOST-C 10.10.10.12 204.79.197.200 443

Example 3: XDR event log preset

Goal: Analyze forensic event log data using the xdr_event_log preset.

XQL code:

config timeframe = 30d
| preset = xdr_event_log
| filter EVTX_Event_ID = 104
| fields host_name, Timestamp, message, EVTX_Event_ID
| limit 10

Explanation: This query uses the xdr_event_log preset to normalize and view event logs, specifically filtering for Windows System Event ID 104 (Log cleared). An optimized alternative would query dataset = xdr_data filtering for event_type = ENUM.EVENT_LOG.

Output:

HOST_NAME TIMESTAMP MESSAGE EVTX_EVENT_ID
DC-01 1698304800000 The System log file was cleared. 104
DC-02 1698305130000 The System log file was cleared. 104

Example 4: XDR file preset

Goal: Investigate file-related events such as writes or accesses using the xdr_file preset.

XQL code:

config timeframe = 7d
| preset = xdr_file
| filter action_file_extension = "exe"
| fields event_id, action_file_name, action_file_path
| limit 10

Explanation: This query uses the xdr_file preset to focus on file activities, filtering specifically for executable files. An optimized alternative involves querying dataset = xdr_data with filter event_type = ENUM.FILE.

Output:

EVENT_ID ACTION_FILE_NAME ACTION_FILE_PATH
ev_101 update.exe C:\Temp\update.exe
ev_102 malware.exe C:\Users\Public\malware.exe

Example 5: Host inventory users preset

Goal: Retrieve information about users discovered on endpoints using the host_inventory_users preset.

XQL code:

config timeframe = 30d
| preset = host_inventory_users
| filter disabled = true
| fields endpoint_name, name, full_name, disabled
| limit 10

Explanation: This query leverages the host_inventory_users preset (requiring Host Insights) to identify disabled user accounts on endpoints.

Output:

ENDPOINT_NAME NAME FULL_NAME DISABLED
LAPTOP-55 Guest Guest User true
SERVER-01 old_admin Old Administrator true

Example 6: Host inventory shares preset

Goal: View details about shared resources on hosts using the host_inventory_shares preset.

XQL code:

config case_sensitive = false timeframe = 30d
| preset = host_inventory_shares
| filter description not contains "print"
| fields endpoint_name, description, share_type
| limit 5

Explanation: This query uses the host_inventory_shares preset to list shared resources, filtering out print shares to focus on other types of shares.

Output:

ENDPOINT_NAME DESCRIPTION SHARE_TYPE
FILE-SERVER Finance Docs Disk
BACKUP-SRV Daily Backups Disk

Example 7: Host users to groups preset

Goal: Analyze user-to-group mappings using the host_users_to_groups preset.

XQL code:

config case_sensitive = false timeframe = 30d
| preset = host_users_to_groups
| fields endpoint_name, user_name, group_name
| limit 5

Explanation: This query uses the host_users_to_groups preset (derived from Host Insights) to view the relationships between users and groups on endpoints.

Output:

ENDPOINT_NAME USER_NAME GROUP_NAME
WORKSTATION-A alice Administrators
WORKSTATION-A alice Remote Desktop Users

Example 8: Device control preset

Goal: Monitor USB device connections using the device_control preset.

XQL code:

config timeframe = 7d
| preset = device_control
| filter action_device_type = "USB"
| fields agent_hostname, action_device_id, action_device_info
| limit 10

Explanation: This query utilizes the device_control preset to view events related to USB device connections and disconnections.

Output:

AGENT_HOSTNAME ACTION_DEVICE_ID ACTION_DEVICE_INFO
USER-PC-01 USB\VID_0781&PID_5581 SanDisk Ultra
LAB-PC-04 USB\VID_0951&PID_1666 Kingston DataTraveler

replacenull

Use the replacenull stage to ensure data completeness by replacing null (or null-equivalent) values within a specified field with a default value. The stage is schema-aware: it uses the field's metadata type to determine which replacement value is valid, and supports both primitive types (String, Number, Boolean, Enum) and complex types (Array, JSON, Datetime).

Syntax

replacenull <field> = <replacement_value>

Parameters

Name Type Required Description
field string Yes The name of the field whose null (or null-equivalent) values you want to replace.
replacement_value string, number, boolean, array, JSON, or datetime Yes The value that replaces any NULL (or null-equivalent) occurrences in the specified field. The value must match the data type of the field. Literal strings must be enclosed in double quotes, and complex types must be constructed with the appropriate function (see Usage notes).

Returns

The replacenull stage returns the dataset with the specified field modified, where all NULL (and null-equivalent) values have been substituted with the provided replacement value. All other records and fields are returned unchanged.

Usage notes

  • The replacenull stage is schema-aware. It reads the field's metadata type before applying the replacement logic, so the replacement_value you provide must be compatible with the field's data type.
  • The stage supports the following field types, each with its own replacement rules:

    Field type Null-equivalent condition How to specify the replacement value
    String NULL A quoted string literal, for example "invalid host".
    Number NULL A numeric literal, for example 0.
    Boolean NULL true or false.
    Enum NULL A valid enum value.
    Array NULL or empty array [] An array constructed with arraycreate(), for example arraycreate("no-record").
    JSON NULL A JSON object, typically an empty object {} or one constructed with object_create().
    Datetime NULL A quoted datetime string in a supported format (see below).
  • Empty array detection: For Array fields, both NULL values and empty arrays ([]) are treated as null-equivalent and are replaced.
  • Datetime formats: For Datetime fields, the replacement value must be provided as a quoted string in one of the following supported formats:
    • ISO 8601 with Z: %Y-%m-%dT%H:%M:%SZ (for example, "1970-01-01T00:00:00Z")
    • ISO 8601 without Z: %Y-%m-%dT%H:%M:%S (for example, "1970-01-01T00:00:00")
    • Standard format: %Y-%m-%d %H:%M:%S (for example, "1970-01-01 00:00:00")
  • If you employ the replacenull stage, any subsequent stages that refer to the modified field's value must use the newly defined replacement value instead of checking for NULL.
  • Apply filter stages as early as possible in your query. This reduces the total dataset size before replacenull processes the records, minimizing the amount of data it needs to operate on.
  • Use the fields stage immediately after initial filtering to select only the necessary columns. This reduces the data footprint passed to replacenull and subsequent stages, improving overall query performance.

Examples

Example 1: Basic replacenull on a String field

Goal: Replace NULL values in a primitive String field with a default label.

XQL code:

dataset = xdr_data
| comp count() as count by agent_hostname
| replacenull agent_hostname = "invalid host"
| limit 100

Explanation: The replacenull agent_hostname = "invalid host" stage replaces any NULL values in the agent_hostname field with the string "invalid host".

Output:

COUNT AGENT_HOSTNAME
42 server-01
15 invalid host
7 server-03

Example 2: replacenull on an Array field (empty arrays and nulls)

Goal: Replace both NULL values and empty arrays ([]) in an Array field with a default array.

XQL code:

dataset = xdr_data
| fields host, dns_records
| replacenull dns_records = arraycreate("no-record")
| limit 100

Explanation: Because dns_records is an Array field, both NULL values and empty arrays are treated as null-equivalent. The replacenull dns_records = arraycreate("no-record") stage replaces those values with the array ["no-record"].

Before replacenull:

HOST DNS_RECORDS
server-01 ["a.example.com", "b.example.com"]
server-02 null
server-03 []

After replacenull:

HOST DNS_RECORDS
server-01 ["a.example.com", "b.example.com"]
server-02 ["no-record"]
server-03 ["no-record"]

Example 3: Type-specific replacements across multiple field types

Goal: Apply replacenull to several fields of different types — String, Number, Datetime, and JSON — in a single query, using a replacement value appropriate to each type.

XQL code:

dataset = xdr_data
| replacenull host = "N/A"
| replacenull severity = 0
| replacenull event_time = "1970-01-01T00:00:00Z"
| replacenull metadata = object_create("env", "dev")
| limit 100

Explanation: Each replacenull stage targets a field of a specific type:

  • host (String) — NULL values are replaced with "N/A".
  • severity (Number) — NULL values are replaced with 0.
  • event_time (Datetime) — NULL values are replaced with the ISO 8601 datetime "1970-01-01T00:00:00Z".
  • metadata (JSON) — NULL values are replaced with the object {"env": "dev"} constructed by object_create().

Before replacenull:

HOST SEVERITY EVENT_TIME METADATA
server-01 null 2026-03-24T10:30:00Z null
null 5 null {"env": "prod"}
null null null {}

After replacenull:

HOST SEVERITY EVENT_TIME METADATA
server-01 0 2026-03-24T10:30:00Z {"env": "dev"}
N/A 5 1970-01-01T00:00:00Z {"env": "prod"}
N/A 0 1970-01-01T00:00:00Z {}

Example 4: replacenull on a field derived from a JSON function

Goal: Replace NULL values in a field created by an alter stage using a JSON function, where the extracted value can be NULL if the key does not exist.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, simple_json_data // Select relevant fields, including the JSON source
| alter json_code = json_extract_scalar(simple_json_data, "$.code") // Extract 'code'; can be NULL
| replacenull json_code = "Code Not Found" // Replace NULLs in the new 'json_code' field
| limit 5

Explanation: The query extracts the value of the "code" key using json_extract_scalar(simple_json_data, "$.code"). For records where this key does not exist (102, 103, 104, 105), the result is NULL. The replacenull json_code = "Code Not Found" stage then replaces these NULL values.

Output:

EVENT_ID SIMPLE_JSON_DATA JSON_CODE
101 {"status": "ok", "code": 200} 200
102 {"status": "fail", "error": "access_denied"} Code Not Found
103 {"connection_id": "CONN-001", "protocol": "TCP"} Code Not Found
104 {"health": "good"} Code Not Found
105 {"transform_stage": 1} Code Not Found

Example 5: Filtering based on the replaced value

Goal: Demonstrate how subsequent filter stages must use the replacement value when querying for values that were originally NULL.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, ipv4_address // Select necessary fields
| replacenull ipv4_address = "UNKNOWN_IP" // Replace NULLs in ipv4_address
| filter ipv4_address = "UNKNOWN_IP" // Filter using the replacement value
| limit 5

Explanation: The replacenull ipv4_address = "UNKNOWN_IP" stage replaces NULL entries in ipv4_address. Since ipv4_address is NULL for event_id 103, 107, and 109, the subsequent filter filter ipv4_address = "UNKNOWN_IP" successfully identifies and returns only those records.

Output:

EVENT_ID IPV4_ADDRESS
103 UNKNOWN_IP
107 UNKNOWN_IP
109 UNKNOWN_IP
  • Stages: alter, filter, fields, comp
  • Functions: arraycreate, object_create, json_extract_scalar, coalesce

search

Use the search stage to perform free-text string searching across your ingested data, allowing you to find specified text strings within fields of single or multiple datasets.

Syntax

search "<free_text1>"[,"<free_text2>", ...] [mode="<search_mode>"] [dataset = <dataset name>]

Parameters

Name Type Required Description
<free_text> string Yes The text string to search for. Multiple strings imply an OR condition.
mode string No Scopes the investigation to route execution. Scoping the query accelerates response times for complex queries and provides granular control over the query. Accepted values are raw, normalize, and all. Syntax: mode="search_mode". See Search modes.
dataset dataset No Refines the search by explicitly specifying a dataset.

Search modes

The mode parameter accepts the following values:

Mode Description
raw Searches across all columns in raw datasets. This is the default mode for general and wildcard searches.
normalize Restricts the search to normalized datasets (such as xdr_data). For maximum performance, this mode searches string columns only and explicitly bypasses enum fields.
all Searches across all datasets in the tenant. This is the legacy behavior.

Returns

The search stage returns records containing the specified text string(s). When searching a single dataset, all field columns of that dataset are included. When searching multiple datasets, the results include key columns such as _time, _vendor, _product, _dataset, and raw_data.

Usage notes

  • The search stage must be the first stage in your XQL query, though it can be preceded by a config stage.
  • Queries containing the search stage do not support aggregation stages such as bin, comp, top, or dedup.
  • Queries using the search stage are limited to the last 90 days of data.
  • By default, forensic datasets are not included in search stage queries unless specifically enabled.
  • If searching multiple datasets, the raw_data field column contains the JSON with the relevant raw information, allowing for drill-down.
  • Syntax limitation: The mode parameter cannot be combined with a specific, non-wildcard dataset. For example, search "login failure" mode=normalize dataset = xdr_data returns an error. If you know the exact dataset, query it directly without the mode parameter.
  • Optimized search volume: The raw and normalize modes are optimal for 1 to 10 distinct terms, such as specific IPs or hostnames. For heavy workflows that span large tenants across all datasets, use the all mode.

Syntax examples

The following tables summarize valid and invalid combinations of the search stage syntax.

Use cases

Use case Example Result
Basic default search search "login failure" Automatically defaults to the raw mode and searches across wildcard tables
Explicit mode definition search "login failure" mode=all Forces the search across both raw and normalized datasets
Explicit mode with wildcard-only dataset search "login failure" mode=raw dataset = * Explicitly assigns the mode while searching all wildcard tables
Using modes with wildcard datasets search "login failure" mode=normalize dataset = xdr_* Explicitly scopes the normalized mode to datasets matching the wildcard
Searching multiple wildcard datasets search "login failure" mode=normalize dataset in (xdr_*, panw_*) Applies the performance mode across multiple wildcard dataset patterns
Implicit mode assignment (wildcard) search "login failure" dataset = xdr_* Because a wildcard is used without an explicit mode, this automatically defaults to the raw mode
Specific dataset query (no mode needed) search "login failure" dataset = xdr_data Directly queries a specific dataset. The mode parameter is intentionally left blank
Piping to additional filters search "login failure" mode=raw \| filter agent_os = WINDOWS Quickly searches raw datasets and filters the subsequent results

Invalid use cases

Use case Example Result
Mode combined with a specific dataset (invalid) search "login failure" mode=normalize dataset = xdr_data Returns an error: the mode parameter is not allowed when querying a specific, non-wildcard dataset
Multiple modes provided (invalid) search "login failure" mode=raw mode=all Returns an error for a duplicate argument
Mode combined with a list of specific datasets (invalid) search "login failure" mode=raw dataset in (xdr_data, panw_data) Returns an error: wildcards must be present in the dataset list when using a mode

Examples

Example 1: Basic free-text search across all datasets

Goal: Searches for the phrase "login successful" across all available datasets in the tenant (implicitly).

XQL code:

search "login successful" mode=all

Explanation: This query searches for the string "login successful" across all datasets. In the context of the sample data, it finds the relevant login event.

Output:

_time _dataset event_description raw_log_data
2023-10-26 10:00:00 UTC sample_xql_raw "User login successful" "User Alice logged in from 192.168.1.10"

Example 2: Free-text search within a specific dataset

Goal: Explicitly searches for the string "cmd.exe" only within the sample_xql_raw dataset.

XQL code:

search "cmd.exe" dataset = sample_xql_raw

Explanation: This query restricts the free-text search for "cmd.exe" to the specified sample_xql_raw dataset.

Output:

_time _dataset event_description raw_log_data
2023-10-26 10:05:30 UTC sample_xql_raw "File access attempt" "Process cmd.exe attempted to access /etc/passwd"

Example 3: Searching for multiple free-text strings

Goal: Searches for events containing either "connection" or "backup" within the sample_xql_raw dataset.

XQL code:

search "connection", "backup" dataset = sample_xql_raw

Explanation: This query searches for records containing either "connection" OR "backup" within the specified dataset.

Output:

_time _dataset event_description raw_log_data
2023-10-26 10:15:15 UTC sample_xql_raw "Network connection established" "Outbound connection to 1.1.1.1:443 initiated by AppX"
2023-10-26 11:00:10 UTC sample_xql_raw "Database backup completed" "Full backup of prod_db to S3 completed."

Goal: Searches for "successful" events within the last 24 hours.

XQL code:

config timeframe = 24h
search "successful" dataset = sample_xql_raw

Explanation: The config timeframe stage is used before the search stage to limit the query execution window to the last 24 hours.

Output:

_time _dataset event_description is_successful
2023-10-26 10:00:00 UTC sample_xql_raw "User login successful" true

Example 5: Searching raw datasets

Goal: Searches for the phrase "login failure" across all columns in raw datasets.

XQL code:

search "login failure"

Explanation: The default mode for the search stage is raw. The default mode scopes the search to all columns in raw datasets.

Output:

_time _dataset event_description raw_log_data
2023-10-26 10:20:00 UTC sample_xql_raw "User login failure" "User Bob failed to log in from 10.0.0.5"

Example 6: Restricting the search to normalized datasets using the Normalize mode

Goal: Searches for the phrase "login failure" within normalized datasets only.

XQL code:

search "login failure" mode="normalize"

Explanation: The mode="normalize" parameter restricts the search to normalized datasets (such as xdr_data). For maximum performance, this mode searches string columns only and explicitly bypasses enum fields.

Output:

_time _dataset event_description raw_log_data
2023-10-26 10:20:00 UTC xdr_data "User login failure" "User Bob failed to log in from 10.0.0.5"

sort

Use the sort stage to identify the sort order for records returned in the result set.

Syntax

sort asc|desc <field1>[, asc|desc <field2>...]

Parameters

Name Type Required Description
asc keyword No Sorts records in ascending order (lowest to highest).
desc keyword No Sorts records in descending order (highest to lowest).
field string Yes Specifies one or more fields to sort by. If multiple fields are provided, records are sorted in the order the fields are specified.

Returns

The sort stage returns the query result set organized according to the specified order.

Usage notes

  • Be aware that the union and join stages do not preserve sort order. If you require a specific sort order after combining datasets, always place the sort stage after your union or join stage.
  • For correct sorting results when a query includes strings representing numbers, it is recommended to convert all string fields to integers or numbers (for example, using to_integer() or to_number() functions) before applying the sort stage.
  • When sorting by multiple columns, while the sort operation is saved correctly, the user interface will only display the results according to the first sorted column.
  • Sort operations can concentrate data on a single worker, which for large datasets might exceed its capacity. Including a limit stage after a sort stage can help reduce the maximum memory a worker needs to devote to the sort task, thereby improving performance.

Examples

Example 1: Single field sort (ascending)

Goal: Sorts records by event_id in ascending order.

XQL code:

dataset = sample_xql_raw
| sort asc event_id
| fields event_id, event_description

Explanation: This query sorts the events by their unique event_id in ascending order (lowest to highest).

Output:

EVENT_ID EVENT_DESCRIPTION
101 User login successful
102 File access attempt
103 Network connection established
104 System heartbeat
105 Data transformation
106 Unauthorized access detected
107 Cloud resource modification
108 Software update initiated
109 API request throttled
110 Database backup completed

Example 2: Single field sort (descending)

Goal: Sorts records by duration_seconds in descending order.

XQL code:

dataset = sample_xql_raw
| sort desc duration_seconds
| fields event_id, event_description, duration_seconds

Explanation: This example sorts events by duration_seconds in descending order, showing the longest duration events first.

Output:

EVENT_ID EVENT_DESCRIPTION DURATION_SECONDS
110 Database backup completed 60.0
108 Software update initiated 15.3
103 Network connection established 10.2
107 Cloud resource modification 7.8
105 Data transformation 5.0
106 Unauthorized access detected 2.1
101 User login successful 1.5
102 File access attempt 0.8
104 System heartbeat 0.1
109 API request throttled 0.05

Example 3: Multiple field sort

Goal: Sorts records by is_successful (desc) then duration_seconds (asc).

XQL code:

dataset = sample_xql_raw
| sort desc is_successful, asc duration_seconds
| fields event_id, event_description, is_successful, duration_seconds

Explanation: This example first sorts by is_successful (placing true results first due to descending sort order on boolean), and then by duration_seconds in ascending order for events with the same is_successful status.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL DURATION_SECONDS
104 System heartbeat true 0.1
101 User login successful true 1.5
105 Data transformation true 5.0
107 Cloud resource modification true 7.8
103 Network connection established true 10.2
108 Software update initiated true 15.3
110 Database backup completed true 60.0
109 API request throttled false 0.05
102 File access attempt false 0.8
106 Unauthorized access detected false 2.1

Example 4: Numeric conversion sort

Goal: Extracts a numeric code from JSON and sorts by it in ascending order.

XQL code:

dataset = sample_xql_raw
| alter status_code = to_number(coalesce(json_extract_scalar(simple_json_data, "$.code"), json_extract_scalar(simple_json_data, "$.error_code")))
| filter status_code != null // Ensure we only sort non-null numeric codes
| sort asc status_code
| fields event_id, event_description, simple_json_data, status_code

Explanation: This example demonstrates how to extract a numeric value (either code or error_code) from the simple_json_data JSON field, convert it to a number using to_number(), and then sort by it. This ensures numeric sorting rather than string sorting (for example, ensuring 200 comes before 429).

Output:

EVENT_ID EVENT_DESCRIPTION SIMPLE_JSON_DATA STATUS_CODE
101 User login successful {"status": "ok", "code": 200} 200
109 API request throttled {"error_code": 429, "message": "Rate limit exceeded"} 429

tag

Use the tag stage to augment your data by adding custom labels to records. These labels are appended to the _tag system field, making it easier to categorize and search for specific events later.

Syntax

tag add <tag name>
tag add "<tag name1>", "<tag name2>", ...

Parameters

Name Type Required Description
add keyword Yes The command operation to append a tag.
<tag name> string Yes The string value to be added as a tag. Must be enclosed in quotes if it contains spaces or special characters.

Returns

The tag stage returns the original records, enriched with the specified string values appended to the _tag system field.

Usage notes

  • The tag stage specifically modifies the _tag system field. Tags can only be applied to this field.
  • While the tag stage itself is a straightforward operation, general XQL optimization principles should be applied to the query as a whole.
  • Apply filter stages as early as possible in your query to reduce the dataset size before the tag stage processes the records.
  • Utilize the fields stage immediately after initial filtering to select only the necessary columns. This minimizes the data footprint passed to tag and subsequent stages.
  • Always use the smallest practical timeframe to limit data scanning.

Examples

Example 1: Adding a single tag to records

Goal: Adds "audit_processed" to the _tag field for all records.

XQL code:

config timeframe = 1d 
| dataset = sample_xql_raw 
| fields event_id, event_description 
| tag add "audit_processed" 
| limit 3 

Explanation: The tag add "audit_processed" stage appends the string "audit_processed" to the _tag system field for all records that pass through this stage.

Output:

EVENT_ID EVENT_DESCRIPTION
101 "User login successful"
102 "File access attempt"
103 "Network connection established"

Example 2: Adding a list of tags to records

Goal: Adds "security_incident" and "review_needed" to _tag for unsuccessful events.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter is_successful = false 
| fields event_id, event_description, is_successful 
| tag add "security_incident", "review_needed" 
| limit 3

Explanation: The filter is_successful = false stage first narrows down the dataset to only unsuccessful events. The tag add "security_incident", "review_needed" stage then applies both specified tags to the _tag system field of these filtered records.

Output:

EVENT_ID EVENT_DESCRIPTION IS_SUCCESSFUL
102 "File access attempt" false
106 "Unauthorized access detected" false
109 "API request throttled" false

target

Use the target stage to persist the results of your query to a new or existing dataset or lookup table. This allows the results to be used in subsequent queries, facilitating complex data pipelines and long-term storage.

Syntax

target type=dataset|lookup [append=true|false] <dataset name>

Parameters

Name Type Required Description
type string Yes Defines the kind of storage for your query results. Supported values are dataset (persistent storage) or lookup (small reference tables).
append boolean No Controls how results are added to an existing dataset or lookup table. true appends results; false (default) overwrites existing data.
dataset name string Yes A unique name for your new or existing dataset or lookup table.

Returns

The target stage persists the query results to the specified storage destination (dataset or lookup table) and allows them to be referenced in future queries.

Usage notes

  • The target stage must be the final stage in your XQL query. You cannot place any other stages after it.
  • Datasets created via the target stage are persistent and can be queried later using the standard dataset = <dataset name> syntax.
  • For very long time windows or complex operations, running multiple smaller queries and appending their results to a working dataset using target type=dataset append=true can significantly outperform a single, long-running query.
  • Be mindful of the size limit for lookup tables, which is 50 MB (or 30 MB if uploaded via the Dataset Management page).

Examples

Example 1: Create new dataset (implicit append)

Goal: Save all successful login events to a new dataset called my_successful_logins.

XQL code:

dataset = sample_xql_raw
| filter event_description = "User login successful" and is_successful = true
| fields event_id, event_description, _time
| target type=dataset my_successful_logins

Explanation: The query filters the sample_xql_raw dataset for successful user logins and saves the selected fields to a new persistent dataset named my_successful_logins. Since append is not specified, it defaults to false (overwrite) behavior when creating new, but functionally acts as a creation step.

Output:

event_id event_description _time
101 "User login successful" 2023-10-26 10:00:00 UTC

Example 2: Explicitly creating a new dataset (append=false)

Goal: Save network connection durations to a dataset named event_durations_summary, explicitly overwriting any existing data.

XQL code:

dataset = sample_xql_raw
| filter event_description = "Network connection established"
| fields event_id, duration_seconds, _time
| target type=dataset append=false event_durations_summary

Explanation: This query filters for network connection events and saves them to event_durations_summary. The append=false parameter ensures that if this dataset already exists, its previous content is completely overwritten with these new results.

Output:

event_id duration_seconds _time
103 10.2 2023-10-26 10:15:15 UTC

Example 3: Appending to an existing dataset (append=true)

Goal: Append "Database backup completed" events to the previously created my_successful_logins dataset.

XQL code:

dataset = sample_xql_raw
| filter event_description = "Database backup completed" and is_successful = true
| fields event_id, event_description, _time
| target type=dataset append=true my_successful_logins

Explanation: This query filters for successful database backup events. The append=true parameter appends these new records to the existing my_successful_logins dataset (created in Example 1) instead of overwriting it.

Output:

event_id event_description _time
101 "User login successful" 2023-10-26 10:00:00 UTC
110 "Database backup completed" 2023-10-26 11:00:10 UTC

Example 4: Creating a lookup table (type=lookup)

Goal: Create a small lookup table named event_codes_lookup containing event_id and extracted status codes.

XQL code:

dataset = sample_xql_raw
| alter status_code = to_number(json_extract_scalar(simple_json_data, "$.code"))
| filter status_code != null
| fields event_id, status_code
| target type=lookup event_codes_lookup

Explanation: The query extracts status codes from the JSON data, filters out nulls, and saves the event_id and status_code mapping to a lookup table named event_codes_lookup. Lookup tables are optimized for small, static reference data.

Output:

event_id status_code
101 200
109 429

top

Use the top stage to identify and return the most frequently occurring (or highest-sum) elements for a given field. The stage provides approximate counts and percentages, making it scalable for large datasets.

Syntax

top <integer> <field> [by <field1> ,<field2>...] [top_count as <column name>, top_percent as <column name>]

Parameters

Name Type Required Description
integer integer No Represents the number of top elements to return. If omitted, it defaults to up to 10 elements.
field string Yes The field for which to find the top elements.
by field1, ... string No An optional clause to group rows based on specified fields before identifying top elements within each group.
top_count as ... string No Optional alias to rename the default TOP_COUNT result column.
top_percent as ... string No Optional alias to rename the default TOP_PERCENT result column.

Returns

The top stage returns a result set containing the specified fields, along with TOP_COUNT (the approximate count of the value) and TOP_PERCENT (the percentage of the value relative to the total).

Usage notes

  • The top stage produces approximate results, which are optimized for memory usage and time, particularly beneficial when dealing with vast amounts of data.
  • Early Filtering: Apply filter stages as early as possible in your query to reduce the dataset size before the top stage processes the records.
  • Precise Field Selection: Utilize the fields stage immediately after initial filtering to select only the necessary columns. This minimizes the data footprint passed to top and subsequent stages, improving overall query performance.
  • Timeframe Management: Always use the smallest practical timeframe to limit data scanning.

Examples

Example 1: Basic top stage example

Goal: Identifies the top 3 most frequent event descriptions by count.

XQL code:

config timeframe = 1d // Use a practical timeframe
| dataset = sample_xql_raw // Specify the dataset
| fields event_id, event_description // Select relevant fields
| top 3 event_description // Find the top 3 event descriptions by frequency
| limit 5 // Limit results for brevity

Explanation: The query calculates the approximate frequency of each unique event_description and returns the top 3 based on that frequency. The query also provides the percentage of each in the total.

Output:

event_description TOP_COUNT TOP_PERCENT
"User login successful" 1 ~10.0%
"File access attempt" 1 ~10.0%
"Network connection established" 1 ~10.0%

Example 2: Top stage with by clause

Goal: Finds the top 1 event description for each 'is_successful' status.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields is_successful, event_description // Select relevant fields
| top 1 event_description by is_successful // Find the top 1 event description within each 'is_successful' group
| limit 5

Explanation: The by is_successful clause groups the records first by their is_successful status, and then top 1 event_description is applied independently to each of these groups.

Output:

is_successful event_description TOP_COUNT TOP_PERCENT
true "User login successful" 1 ~16.7%
false "File access attempt" 1 ~33.3%

Example 3: Top stage with custom column names

Goal: Shows the top successful/unsuccessful statuses with custom count/percentage names.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields is_successful // Focus on the boolean field for clear counting
| top is_successful top_count as StatusCount, top_percent as StatusPercentage // Use custom column names
| limit 5

Explanation: The query identifies the frequencies of true and false in the is_successful field. The top_count as StatusCount and top_percent as StatusPercentage clauses rename the output columns as specified, enhancing readability.

Output:

is_successful StatusCount StatusPercentage
true 7 70.0%
false 3 30.0%

transaction

Use the transaction stage to group related events into logical sequences or "transactions" based on specified fields. This stage aggregates all fields from the individual events into JSON string arrays and calculates summary statistics for each sequence, such as duration and event count.

Syntax

transaction <field_1>, <field_2>, ... [span = <time> [timeshift = <epoch time> [timezone = "<time zone>"]] | startswith = <condition> endswith = <condition> [allowunclosed = true|false]] [maxevents = <number>]

Parameters

Name Type Required Description
field_n string Yes One or more fields used to define the groups for the transaction.
span string No Sets a maximum time frame for each transaction (for example, 30m, 1h).
timeshift integer No Defines a specific start time (epoch) for grouping events when using span.
timezone string No Defines the time zone for the timeshift parameter (for example, "+00:00").
startswith boolean expression No A condition defining the event that starts a transaction.
endswith boolean expression No A condition defining the event that ends a transaction.
allowunclosed boolean No If true (default), includes transactions that do not have a matching ending event.
maxevents integer No Defines the maximum number of events to include per transaction. The default is 100.

Returns

The transaction stage returns aggregated records representing the grouped sequences. All original fields from the grouped events are aggregated into JSON string arrays.

Additionally, the stage appends the following system fields to each transaction record:

  • _start_time: The timestamp of the first event in the transaction.
  • _end_time: The timestamp of the last event in the transaction.
  • _duration: The difference in seconds between the start and end timestamps.
  • _num_of_rows: The count of events included in the transaction.
  • _transaction_id: A unique identifier for the transaction.

Usage notes

  • The transaction stage aggregates all fields in a JSON string array based on the transaction fields.
  • A maximum of 50 fields can be aggregated in a single transaction stage.
  • Transaction operations can be resource-intensive, especially on large datasets. To optimize performance, apply filter stages as early as possible to reduce the dataset size before the transaction stage processes records.
  • Use the fields stage immediately after initial filtering to select only the necessary columns, minimizing the data footprint passed to the transaction stage.
  • When using allowunclosed = true (the default), a transaction that does not find a matching ending event will define its last event as 12 hours after the starting event.

Examples

Example 1: Basic transaction grouping

Goal: Group events into transactions based on identical values in the event_id and event_description fields.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| transaction event_id, event_description
| limit 5

Explanation: This query groups rows that share identical values for both event_id and event_description into a single transaction. Since these fields are unique in the sample data, each row forms its own transaction.

Output:

EVENT_ID EVENT_DESCRIPTION _START_TIME _END_TIME _DURATION _NUM_OF_ROWS _TRANSACTION_ID
101 "User login successful" 2023-10-26 10:00:00 UTC 2023-10-26 10:00:00 UTC 0 1 ...
102 "File access attempt" 2023-10-26 10:05:30 UTC 2023-10-26 10:05:30 UTC 0 1 ...

Example 2: Transaction with time span

Goal: Group successful events into transactions that occur within a 1-hour time window.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| filter is_successful = true
| transaction is_successful span=1h
| limit 5

Explanation: The query filters for successful events and then groups them into a single transaction if they occur within the specified 1-hour span. In the sample data, multiple successful events fall within this window.

Output:

IS_SUCCESSFUL _START_TIME _END_TIME _DURATION _NUM_OF_ROWS _TRANSACTION_ID
true 2023-10-26 10:00:00 UTC 2023-10-26 11:00:10 UTC ~3610 7 ...

Example 3: Transaction with timeshift and timezone

Goal: Group successful events within 30-minute windows, aligned to a specific start time and timezone.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| transaction is_successful span=30m timeshift=1698304800 timezone="+00:00"
| limit 5

Explanation: This query groups events by is_successful into 30-minute windows. The windows are calculated relative to the timeshift epoch (corresponding to 2023-10-26 10:00:00 UTC).

Output:

IS_SUCCESSFUL _START_TIME _END_TIME _DURATION _NUM_OF_ROWS _TRANSACTION_ID
true 2023-10-26 10:00:00 UTC 2023-10-26 10:30:45 UTC ~1845 4 ...
true 2023-10-26 10:45:00 UTC 2023-10-26 11:00:10 UTC ~910 3 ...
false 2023-10-26 10:05:30 UTC 2023-10-26 10:05:30 UTC 0 1 ...
false 2023-10-26 10:40:10 UTC 2023-10-26 10:55:55 UTC ~945 2 ...

Example 4: Transaction with start and end conditions

Goal: Identify transactions that begin with a log entry containing "User" and end with one containing "S3".

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| transaction raw_log_data startswith="User" endswith="S3"
| limit 5

Explanation: The query groups events based on the raw_log_data field. A transaction starts when a value contains "User" and ends when a value contains "S3".

Output:

RAW_LOG_DATA _START_TIME _END_TIME _DURATION _NUM_OF_ROWS _TRANSACTION_ID
"User Alice logged in..." 2023-10-26 10:00:00 UTC 2023-10-26 10:00:00 UTC 0 1 ...
"Full backup of prod_db to S3..." 2023-10-26 11:00:10 UTC 2023-10-26 11:00:10 UTC 0 1 ...

Example 5: Transaction with max events limit

Goal: Group events by success status but strictly limit each transaction to a single event.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| transaction is_successful maxevents=1
| limit 5

Explanation: By setting maxevents=1, the query forces every event into its own distinct transaction, regardless of whether they share the same is_successful status or fall within a time span.

Output:

IS_SUCCESSFUL _START_TIME _END_TIME _DURATION _NUM_OF_ROWS _TRANSACTION_ID
true 2023-10-26 10:00:00 UTC 2023-10-26 10:00:00 UTC 0 1 ...
false 2023-10-26 10:05:30 UTC 2023-10-26 10:05:30 UTC 0 1 ...
true 2023-10-26 10:15:15 UTC 2023-10-26 10:15:15 UTC 0 1 ...

transpose

Use the transpose stage to turn rows into columns. Each input row becomes a separate output column, effectively rotating the dataset from a columnar (wide) orientation to a row-based (tall) orientation, converting column headers into data values. This is useful for transforming vertically oriented data into a horizontal layout, making it easier to compare field values across rows side by side.

To maintain system reliability and avoid unpredictable SQL state mutations, the transpose stage enforces several constraints, most notably that it must be the terminal stage of the pipeline (see Usage notes and Limitations).

Syntax

transpose [<max_rows>] [column_name=<string>] [header_field=<field>] [include_empty=<bool>]

Parameters

Name Type Required Description
max_rows integer Yes The maximum number of input rows to transpose into columns. The maximum allowed value is 50. If the user provides a value larger than 50, the number of rows is capped at 50.
column_name string No The name of the output field that contains the original input field names. Default is "column". In the output, this field appears as field_name.
header_field string No The name of an input field whose values are used as the output column names. If not provided, output columns are named "value_1", "value_2", and so on.
include_empty boolean No If set to false, excludes any input field (column) that had no values across all transposed rows. Default is true.

Returns

The transpose stage returns a transformed dataset where:

  • Each input row becomes a separate output column.
  • A new field (named by column_name, default "field_name") is added, containing the original input field names.
  • Output column names are derived from the header_field values if specified, or default to "value_1", "value_2", etc.
  • The number of transposed rows is limited by the max_rows parameter (default 5, maximum 50).
  • If include_empty is false, fields that had no values across all transposed rows are excluded from the output.
  • All transposed values are cast to STRING as the universal data type, regardless of their original type. This ensures the transpose operation succeeds across diverse datasets with mixed column types.
  • Null padding: If the source data contains fewer rows than the specified max_rows limit (for example, transpose 50 called on a 30-row result set), the remaining value columns are automatically filled with NULL values to maintain a consistent output structure.

Usage notes

  • Terminal stage enforcement: The transpose stage must be the last stage in the XQL pipeline. No subsequent stages (such as filter, comp, join, or union) are allowed after transpose. This is because transposing data fundamentally alters the schema — column headers become data and vice versa — making it impractical to apply further transformations on the dynamically generated schema.
  • The transpose stage is a "blocking" operation; it must process all input records before producing any output.
  • You must limit the number of rows to transpose using the max_rows parameter, up to a maximum of 50 rows.
  • In Cortex XSOAR, you must use a limit keyword. For example:
xdr-xql-generic-query query="dataset = management_auditing | pivot count(management_auditing_result) for management_auditing_type in (\"USER\", \"AUTH\") by management_auditing_severity" max_fields="20" query_name="TestPivotQuery" parse_result_file_to_context="false"

or

xdr-xql-generic-query query="dataset =management_auditing | limit 10 | transpose 2" query_name="TestAniQuery"
  • The column_name parameter controls the label of the field that holds the original field names in the transposed output.
  • When header_field is specified, the values of that field in each input row are used as the names of the corresponding output columns, making the output more descriptive.
  • The transpose stage is typically used after aggregation stages (such as comp) or data source stages (such as dataset) to reshape summary data for display or comparison.
  • No ordering guarantee: The order of the output data is based on the previous stage output. If a specific order is needed, use a sort stage before transpose.
  • Graphs are not supported for transposed output.

Limitations

Feature Constraint / Behavior
Terminal Stage Must be the last stage in the XQL pipeline. No subsequent filter, comp, join, or union stages are allowed.
Max Rows 50 rows maximum. The user can provide their own smaller row count limit via the max_rows argument.
Null Padding If source data has fewer rows than the specified limit, remaining value columns are filled with NULL.
Data Typing All transposed values are cast to STRING as the universal data type.
Ordering No ordering guarantee. Data order is based on previous stage output.
Graphs Graphs are not supported for transposed output.

Supported contexts

Context Supported
Correlation No
Parsing Rule No
Dataset RBAC No
Scheduled Queries Yes
Widgets/Reports Yes

Examples

Example 1: Basic transpose

Goal: Transpose a small result set of selected fields.

XQL code:

dataset = xdr_data
| fields id, severity, custom_fields
| transpose 5

Explanation: This query selects three fields and transposes up to 5 rows. Each original field name appears in the field_name column, and each row's values become separate output columns (value_1 through value_5). Since there are only 3 source rows but the limit is 5, the remaining columns (value_4, value_5) are filled with NULL.

Before transpose (3 rows × 3 columns):

id severity custom_fields
evt-001 5 {"priority": "high"}
evt-002 3 {"priority": "low"}
evt-003 8 null

After transpose (3 rows × 6 columns):

field_name value_1 value_2 value_3 value_4 value_5
id "evt-001" "evt-002" "evt-003" null null
severity "5" "3" "8" null null
custom_fields "{"priority": "high"}" "{"priority": "low"}" null null null

Note: All values are cast to STRING in the transposed output, regardless of their original data type.

Example 2: Transpose with custom row limit

Goal: Transpose the first 20 rows into columns.

XQL code:

dataset = xdr_data
| fields src_ip, event_type
| transpose 20

Explanation: This query transposes up to 20 input rows into columns. Each transposed row becomes a column named "value_1" through "value_20", and the original field names appear in the field_name column. If fewer than 20 rows exist in the input, the remaining columns are padded with NULL.

Example 3: Transpose with custom column name and header field

Goal: Transpose rows using a custom label for the field name column and use the sourcetype field values as output column names.

XQL code:

dataset = xdr_data
| fields sourcetype, event_count, severity
| transpose column_name="Test Name" header_field=sourcetype include_empty=false

Explanation: This query transposes the input rows, placing the original field names into a column called "Test Name" instead of the default "field_name". The values of the sourcetype field in each input row are used as the names of the output columns. Any fields that have no values across all transposed rows are excluded because include_empty is set to false.

Output:

Test Name syslog firewall endpoint
event_count 150 42 5
severity low high medium

Example 4: Transpose after aggregation

Goal: Aggregate event counts by type, then transpose the summary for a horizontal comparison.

XQL code:

dataset = xdr_data
| comp count(event_id) as event_count by event_type
| transpose 10

Explanation: This query first aggregates the data to count events by type, then transposes the result so each event type's row becomes a column. This is useful for creating a horizontal summary view of aggregated data. Since transpose must be the terminal stage, no further processing is applied after it.

  • Stages: pivot, comp, fields, sort

union

Use the union stage to combine the result sets of two queries into a single, unified result set. The stage is invaluable for consolidating data from different sources or different filtered views of the same data.

Syntax

union <datasetname>
union (<inner xql query>)

Parameters

Name Type Required Description
datasetname string Yes (Alternative 1) The name of the existing dataset to combine with the current query's result set.
inner xql query query block Yes (Alternative 2) An inner XQL subquery defined inline, whose results will be combined with the current query's result set.

Returns

The union stage returns a single combined result set containing rows from both the parent query and the joined source (dataset or subquery). All fields from both sources become available to subsequent stages.

Usage notes

  • The union stage does not preserve sort order.
  • If a specific order is required for the final output after the union, you must place a sort stage after the union stage.
  • Combining datasets can be resource-intensive, similar to join operations.
  • Always apply filter stages as early as possible in your queries to reduce the amount of data being processed before the union stage.
  • Utilize the fields stage early to select only necessary columns, minimizing data processing.
  • For very large datasets or complex operations, consider breaking a single, long-running query into multiple smaller queries and potentially saving intermediate results to a new dataset using the target stage.

Examples

Example 1: Union with a named dataset

Goal: Combines events from the sample_xql_raw dataset that match "File access attempt" with a conceptually created successful_logins dataset (containing "User login successful" events).

XQL code:

dataset = sample_xql_raw
| filter event_description = "File access attempt"
| fields event_id, event_description, is_successful
| union successful_logins // Assumes 'successful_logins' dataset exists

Explanation: The query filters the current dataset for file access attempts and then uses union to combine these results with all records from the existing successful_logins dataset.

Output:

event_id event_description is_successful
102 File access attempt false
101 User login successful true

Example 2: Union with an Inner XQL Query

Goal: Combines records from sample_xql_raw where is_successful is false with records where duration_seconds is greater than 10.0, derived from a separate inner subquery.

XQL code:

dataset = sample_xql_raw
| filter is_successful = false
| fields event_id, event_description, is_successful, duration_seconds
| union (
    dataset = sample_xql_raw
    | filter duration_seconds > 10.0
    | fields event_id, event_description, is_successful, duration_seconds
)

Explanation: The main query retrieves unsuccessful events. The union stage executes an inner subquery to retrieve events with a duration greater than 10.0 seconds. The results of both queries are combined into a single output.

Output:

event_id event_description is_successful duration_seconds
102 File access attempt false 0.8
106 Unauthorized access detected false 2.1
109 API request throttled false 0.05
103 Network connection established true 10.2
108 Software update initiated true 15.3
110 Database backup completed true 60.0

view

Use the view stage to customize how your query results are displayed, enabling highlighting, graphing, and column reordering.

Syntax

view highlight fields = <field1>[,<field2>,...] values = <value1>[,<value2>,...]
view graph type = <graph_type> xaxis = <field1> yaxis = <field2> [<optional parameters>] [series = <field3>]
view column order = default | populated

Parameters

Name Type Required Description
highlight keyword No Indicates the view operation is to highlight specific text.
fields string Yes (for highlight) A comma-separated list of fields in which to search for the value to highlight.
values string Yes (for highlight) A comma-separated list of string values to highlight within the specified fields.
graph keyword No Indicates the view operation is to generate a graph.
type string Yes (for graph) Specifies the type of graph (for example, pie, column, area, bubble, line, map, scatter).
xaxis string Yes (for graph) The field to be used for the X-axis. Must be collatable.
yaxis string Yes (for graph) The field to be used for the Y-axis. Must be collatable.
series string No Used for certain graph types (area, bubble, column, line, map, scatter) to group chart results based on this field's values.
column order keyword No Indicates the view operation is to reorder columns.
default keyword No Displays columns in their original order.
populated keyword No Displays columns with the most non-null values first.

Returns

The view stage returns the query results formatted according to the specified visualization or ordering rules.

Usage notes

  • For graph types, the fields specified for xaxis and yaxis must be collatable.
  • The series parameter is available for specific graph types (area, bubble, column, line, map, scatter) to group chart results based on yaxis values.
  • The view column order option applies only to the Query Builder's display and is disregarded in widgets, Correlation Rules, public APIs, reports, and dashboards.

Examples

Example 1: Highlighting specific values

Goal: Highlight the text "successful" wherever it appears within the event_description field.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| view highlight fields = event_description values = "successful"

Explanation: This query will display the records and visually highlight the text "successful" in the event_description column (for example, in "User login successful").

Output:

event_id event_description
101 "User login successful"
102 "File access attempt"
103 "Network connection established"

Example 2: Visualizing categorical data (Pie chart)

Goal: Create a pie chart showing the count of events grouped by their is_successful status.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp count() by is_successful as event_count
| view graph type = pie xaxis = is_successful yaxis = event_count

Explanation: This query counts the total number of events for each is_successful status (true or false). The query then displays these counts as a pie chart, where is_successful defines the slices (xaxis) and event_count defines their size (yaxis).

Output:

is_successful event_count
true 7
false 3

Example 3: Trend analysis (column graph)

Goal: Display a column graph showing the count of events per hour.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| bin _time span = 1h
| comp count() as hourly_events by _time
| view graph type = column xaxis = _time yaxis = hourly_events

Explanation: This query groups events into one-hour bins based on _time, counts the hourly_events for each bin, and presents the data as a column graph showing the hourly distribution.

Output:

_time hourly_events
2023-10-26 10:00:00 UTC 9
2023-10-26 11:00:00 UTC 1

Example 4: Adding granularity to graphs (series)

Goal: Show the sum of duration_seconds over time, segmented by is_successful status using a series.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| bin _time span = 1h
| comp sum(duration_seconds) as total_duration by _time, is_successful
| view graph type = column xaxis = _time yaxis = total_duration series = is_successful

Explanation: This query aggregates duration_seconds by hourly _time buckets and is_successful status. The view stage creates a column graph where each hour is represented, segmented by the success status.

Output:

_time is_successful total_duration
2023-10-26 10:00:00 UTC true 39.9
2023-10-26 10:00:00 UTC false 2.95
2023-10-26 11:00:00 UTC true 60.0

Example 5: Prioritizing display (column order)

Goal: Reorder the columns in the results table to display columns with the most non-null values first.

XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| comp count() as event_count by is_successful
| view column order = populated

Explanation: This query counts events by is_successful and then reorders the resulting columns. Columns that are consistently populated appear earlier in the table view.

Output:

is_successful event_count
true 7
false 3

windowcomp

Use the windowcomp stage (short for window computation) to perform analytic (window) functions over a defined window of rows without collapsing or reducing the dataset. Unlike the comp stage, which aggregates rows into summary groups, windowcomp preserves all original rows and appends the computed result as a new field. This is equivalent to SQL OVER(...) window functions and is essential for running totals, rolling averages, ranking, and row-level comparisons against group-level statistics.

Syntax

windowcomp <function>(<field>) [by <partition_field1> [, <partition_field2>, ...]] [sort [asc|desc] <sort_field1> [, [asc|desc] <sort_field2>, ...]] [between <lower> [and <upper>] [frame_type=rows|range]] [as <alias>]

Alternative syntax (using over() clause)

Some navigation functions (first_value, last_value, lag) support an alternative SQL-like syntax:

windowcomp <function>(<field>) as <alias> over(partition by <partition_field> order by <sort_field> [asc|desc])

Parameters

Name Type Required Description
function function Yes The window function to apply (for example, avg, count, sum, max, min, median, rank, row_number, first_value, last_value, lag, stddev_population, stddev_sample).
field string Depends The field to compute over. Required for most functions; optional for count() (counts all rows when omitted) and not used by rank() or row_number().
by clause No Partition-by clause that divides the input rows into independent partitions over which the window function is evaluated. Accepts one or more partition_field values separated by commas. If the by clause is omitted, all rows in the result set are treated as a single partition. Equivalent to SQL PARTITION BY.
sort clause No Sort/order-by clause that defines the ordering of rows within each partition. Accepts one or more sort_field values, each optionally prefixed with asc (ascending, default) or desc (descending). This clause is optional for aggregate functions but required for ranking functions (rank, row_number, dense_rank) and navigation functions (first_value, last_value, lag). When sort is present without a between clause, the default window frame between null and 0 is applied.
between window frame clause clause No Defines a sliding or fixed window frame relative to the current row using the syntax between <lower> and <upper> [frame_type=rows\|range]. The lower and upper bounds specify the range of rows included in the computation: negative integers for preceding rows, 0 for the current row, positive integers for following rows, and null for unbounded boundaries. Numbering functions (rank, row_number, dense_rank) and the lag function cannot be used with this clause. If sort is included but between is not, the default frame between null and 0 is used. If neither sort nor between is specified, the entire partition is the window.
partition_field string No One or more fields used to partition the data into distinct groups (equivalent to SQL PARTITION BY). The by clause breaks up the input field rows into separate partitions, over which the windowcomp function is independently evaluated. Multiple partition fields are allowed. If omitted, all rows in the input table comprise a single partition.
sort_field string No One or more fields that define how rows are ordered within a partition as either ascending (asc) or descending (desc). Defaults to ascending. This clause is optional in most situations, but is required for navigation functions (first_value, last_value, lag) and the rank() function.
lower integer or null No Lower bound of the window frame. 0 = current row, negative integer = rows before the current row, null = unbounded (start of partition). If only a start number is defined (without an end), only a negative number is allowed. If both start and end are defined, the end number must be greater than the start number. Numbering functions and the lag function cannot be used with the window frame clause. If sort is included but the window frame clause is not, the default frame between null and 0 is used.
upper integer or null No Upper bound of the window frame. 0 = current row, positive integer = rows after the current row, null = unbounded (end of partition). The upper boundary must be greater than the lower boundary when both are specified.
frame_type rows or range No Defines the type of window frame. rows (default): Computes the window frame based on physical offsets from the current row (for example, include two rows before and after the current row). To apply the default, nothing needs to be added to the syntax. range: Computes the window frame based on a logical range of rows around the current row, based on the current row's sort key value. The provided range value is added or subtracted to the current row's key value to define a starting or ending range boundary. When using range with start or end numeric nonzero boundaries, exactly one numeric-type sort field is required. When frame_type=range is set, the sort clause must be included; otherwise, only between null and null is supported.
alias string No An optional name for the resulting computed field, assigned using the as clause. If omitted, a default name is generated. When the new field name already exists in the schema, the existing field is replaced with the new computed values.

Returns

The windowcomp stage returns the original dataset with all rows preserved, plus one additional column containing the result of the window function. Each row receives the computed value based on its position within the defined window frame and partition.

Usage notes

  • Row preservation: Unlike the comp stage, windowcomp does not collapse or reduce rows. Every original row is retained, and the computed value is appended as a new field.
  • One function per stage: Only one function can be defined per field within a single windowcomp stage. To compute multiple window functions, chain multiple windowcomp stages.
  • Default window frame: If no between clause is specified and a sort clause is present, the default frame is from the start of the partition to the current row (between null and 0). If no sort clause is specified, the default window encompasses the entire partition.
  • Sort requirement: The sort clause is mandatory for ranking functions (rank, row_number) and is critical when defining a window using the between clause.
  • Null handling: NULL values are generally ignored in aggregate computations (avg, sum, count with a field, min, max, etc.).
  • Partitioning: If no by clause is specified, the window spans the entire result set as a single partition.
  • Between clause: The between clause defines a sliding or fixed window frame relative to the current row. Use negative numbers for preceding rows, 0 for the current row, positive numbers for following rows, and null for unbounded boundaries.
  • Performance: Window computations can be resource-intensive on large datasets. Consider filtering data before applying windowcomp to improve performance.

Supported functions

The following functions are supported within the windowcomp stage:

Aggregate functions

Function Description
avg(<field>) Calculate the average value of a numeric field over the window.
count([<field>]) Count the number of rows (or non-null values of a field) over the window.
max(<field>) Return the maximum value of a field over the window.
min(<field>) Return the minimum value of a field over the window.
median(<field>) Return the median value of a numeric field over the window.
sum(<field>) Compute the sum of a numeric field over the window.

Statistical functions

Function Description
stddev_population(<field>) Compute the population standard deviation of a numeric field over the window.
stddev_sample(<field>) Compute the sample standard deviation of a numeric field over the window (uses Bessel's correction with N-1 denominator).

Ranking functions

Function Description
rank() Assign a rank to each row within a partition. Tied values receive the same rank, and subsequent ranks have gaps.
row_number() Assign a unique sequential integer to each row within a partition, starting at 1.
dense_rank() Assign a rank to each row within a partition. Tied values receive the same rank, and subsequent ranks have no gaps.
Function Description
first_value(<field>) Return the value of a field from the first row in the window frame.
last_value(<field>) Return the value of a field from the last row in the window frame.
lag(<field>) Return the value of a field from the previous row in the partition (based on sort order).
latest(<field>) Retrieve the single chronologically latest value for a field within the window.

Examples

Example 1: Rolling average over a partition

Goal: Calculate the average of duration_seconds for all events within each is_successful group, and display this average for every row. XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, _time, is_successful, duration_seconds
| windowcomp avg(duration_seconds) by is_successful as avg_in_partition
| sort asc is_successful, asc _time, asc event_id
| limit 10

Explanation: The by is_successful clause partitions the data into two groups (true and false). The avg() function calculates the average of duration_seconds for each partition. Since no between clause is specified and no sort is used within the windowcomp, the entire partition is the window. The result is a single average value replicated for every row in that partition. Output:

_time event_id is_successful duration_seconds avg_in_partition
2023-10-26 10:05:30 UTC 102 false 0.8 0.9833
2023-10-26 10:40:10 UTC 106 false 2.1 0.9833
2023-10-26 10:55:55 UTC 109 false 0.05 0.9833
2023-10-26 10:00:00 UTC 101 true 1.5 14.2714
2023-10-26 10:15:15 UTC 103 true 10.2 14.2714
2023-10-26 10:20:00 UTC 104 true 0.1 14.2714
2023-10-26 10:30:45 UTC 105 true 5.0 14.2714
2023-10-26 10:45:00 UTC 107 true 7.8 14.2714
2023-10-26 10:50:20 UTC 108 true 15.3 14.2714
2023-10-26 11:00:10 UTC 110 true 60.0 14.2714

Example 2: Rolling window with between clause

Goal: Calculate a rolling average from the duration_seconds of the current row and the two immediately preceding rows within its partition, ordered by _time. XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, _time, is_successful, duration_seconds
| windowcomp avg(duration_seconds) by is_successful sort asc _time between -2 and 0 as rolling_avg
| sort asc is_successful, asc _time, asc event_id
| limit 10

Explanation: The between -2 and 0 clause defines a rolling window including the current row (0) and the two immediately preceding rows (-2, -1) within its partition. For each row, the avg() function calculates the average of duration_seconds strictly within this rolling window. If there are not enough preceding rows, the window starts from the beginning of the partition. Output:

_time event_id is_successful duration_seconds rolling_avg
2023-10-26 10:05:30 UTC 102 false 0.8 0.8
2023-10-26 10:40:10 UTC 106 false 2.1 1.45
2023-10-26 10:55:55 UTC 109 false 0.05 0.9833
2023-10-26 10:00:00 UTC 101 true 1.5 1.5
2023-10-26 10:15:15 UTC 103 true 10.2 5.85
2023-10-26 10:20:00 UTC 104 true 0.1 3.9333
2023-10-26 10:30:45 UTC 105 true 5.0 5.1
2023-10-26 10:45:00 UTC 107 true 7.8 4.3
2023-10-26 10:50:20 UTC 108 true 15.3 9.3667
2023-10-26 11:00:10 UTC 110 true 60.0 27.7

Example 3: Counting rows in a partition

Goal: Count the total number of events (rows) for all events within each is_successful group, replicating this total count for every row. XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, _time, is_successful, event_description
| windowcomp count() by is_successful sort asc _time as count_in_partition
| sort asc is_successful, asc _time, asc event_id
| limit 10

Explanation: The count() function without a field argument counts all rows in each partition. For the false partition (3 rows), the count is 3 for each row. For the true partition (7 rows), the count is 7 for each row. Output:

_time event_id is_successful event_description count_in_partition
2023-10-26 10:05:30 UTC 102 false "File access attempt" 3
2023-10-26 10:40:10 UTC 106 false "Unauthorized access detected" 3
2023-10-26 10:55:55 UTC 109 false "API request throttled" 3
2023-10-26 10:00:00 UTC 101 true "User login successful" 7
2023-10-26 10:15:15 UTC 103 true "Network connection established" 7
2023-10-26 10:20:00 UTC 104 true "System heartbeat" 7
2023-10-26 10:30:45 UTC 105 true "Data transformation" 7
2023-10-26 10:45:00 UTC 107 true "Cloud resource modification" 7
2023-10-26 10:50:20 UTC 108 true "Software update initiated" 7
2023-10-26 11:00:10 UTC 110 true "Database backup completed" 7

Example 4: Running maximum over time

Goal: Compute a running maximum of bytes sent, ordered by time. XQL code:

dataset = xdr_data
| windowcomp max(bytes_sent) sort asc _time as running_max_bytes

Explanation: By sorting on _time in ascending order, the max() function computes a running maximum of bytes_sent from the start of the dataset up to the current row. Since no by clause is specified, the entire result set is treated as a single partition. Output:

_TIME BYTES_SENT RUNNING_MAX_BYTES
2024-01-15 08:00:00 100 100
2024-01-15 09:00:00 250 250
2024-01-15 10:00:00 150 250
2024-01-15 11:00:00 300 300

Example 5: Cumulative sum (running total)

Goal: Compute a cumulative sum of bytes sent over time. XQL code:

dataset = xdr_data
| windowcomp sum(bytes_sent) sort asc _time as cumulative_bytes

Explanation: The sum() function with sort asc _time and the default frame (between null and 0) produces a cumulative running total of bytes_sent from the start of the dataset up to the current row. Output:

_TIME BYTES_SENT CUMULATIVE_BYTES
2024-01-15 08:00:00 100 100
2024-01-15 09:00:00 250 350
2024-01-15 10:00:00 150 500
2024-01-15 11:00:00 300 800

Example 6: Row numbering within partitions

Goal: Assign a sequential event number to each event per host, ordered by time. XQL code:

dataset = xdr_data
| windowcomp row_number() by agent_hostname sort asc _time as event_seq

Explanation: The row_number() function assigns a unique sequential integer starting at 1 for each row within each agent_hostname partition, ordered by _time. This is useful for identifying the nth event per host. Output:

_TIME AGENT_HOSTNAME EVENT_SEQ
2024-01-15 08:00:00 workstation-1 1
2024-01-15 09:00:00 workstation-1 2
2024-01-15 10:00:00 workstation-1 3
2024-01-15 08:30:00 workstation-2 1
2024-01-15 09:30:00 workstation-2 2

Example 7: Ranking with gaps

Goal: Rank alerts by severity within each host, with gaps for tied values. XQL code:

dataset = xdr_data
| windowcomp rank() by agent_hostname sort desc alert_severity as severity_rank

Explanation: The rank() function assigns a rank to each row within each agent_hostname partition based on alert_severity in descending order. Rows with equal severity receive the same rank, and subsequent ranks have gaps (for example, if two rows share rank 1, the next rank is 3). Output:

_TIME AGENT_HOSTNAME ALERT_SEVERITY SEVERITY_RANK
2024-01-15 09:00:00 workstation-1 7 1
2024-01-15 10:00:00 workstation-1 5 2
2024-01-15 08:00:00 workstation-1 3 3
2024-01-15 09:30:00 workstation-2 4 1
2024-01-15 08:30:00 workstation-2 2 2

Example 8: Fixed window (current row to end of partition)

Goal: Calculate the average for each row over a window that starts from the current row and extends to the end of its partition. XQL code:

config timeframe = 1d
| dataset = sample_xql_raw
| fields event_id, _time, is_successful, duration_seconds
| windowcomp avg(duration_seconds) by is_successful sort asc _time between 0 and null as avg_from_current
| sort asc is_successful, asc _time, asc event_id
| limit 10

Explanation: The between 0 and null clause sets a window starting from the current row (0) through to the very end of the partition (null). For each row, the avg() calculates the average of duration_seconds from the current row's value to the last value in its is_successful partition. Output:

_time event_id is_successful duration_seconds avg_from_current
2023-10-26 10:05:30 UTC 102 false 0.8 0.9833
2023-10-26 10:40:10 UTC 106 false 2.1 1.075
2023-10-26 10:55:55 UTC 109 false 0.05 0.05
2023-10-26 10:00:00 UTC 101 true 1.5 14.2714
2023-10-26 10:15:15 UTC 103 true 10.2 16.4
2023-10-26 10:20:00 UTC 104 true 0.1 17.64
2023-10-26 10:30:45 UTC 105 true 5.0 22.025
2023-10-26 10:45:00 UTC 107 true 7.8 27.7
2023-10-26 10:50:20 UTC 108 true 15.3 37.65
2023-10-26 11:00:10 UTC 110 true 60.0 60.0

Example 9: Sliding window maximum

Goal: Compute the maximum bytes sent within a sliding window of 3 rows (current row plus one row before and one row after). XQL code:

dataset = xdr_data
| windowcomp max(bytes_sent) sort asc _time between -1 and 1 as local_max

Explanation: The between -1 and 1 clause defines a sliding window of 3 rows centered on the current row. The max() function returns the maximum bytes_sent value within that window for each row. Output:

_TIME BYTES_SENT LOCAL_MAX
2024-01-15 08:00:00 100 250
2024-01-15 09:00:00 250 250
2024-01-15 10:00:00 150 300
2024-01-15 11:00:00 300 300

Example 10: First value with over() syntax

Goal: Identify the first process executed on each host by time. XQL code:

dataset = xdr_data
| filter action_process_image_name != null
| windowcomp first_value(action_process_image_name) as initial_process over(partition by agent_hostname order by _time asc)
| fields _time, agent_hostname, action_process_image_name, initial_process
| limit 10

Explanation: The windowcomp stage partitions the events by agent_hostname and orders them chronologically using _time asc. The first_value() function captures the first action_process_image_name encountered in each partition. This value is assigned to the initial_process alias and appended to every row for that host. Output:

_TIME AGENT_HOSTNAME ACTION_PROCESS_IMAGE_NAME INITIAL_PROCESS
2024-01-15 08:00:00 workstation-1 svchost.exe svchost.exe
2024-01-15 09:00:00 workstation-1 chrome.exe svchost.exe
2024-01-15 10:00:00 workstation-1 powershell.exe svchost.exe
2024-01-15 08:30:00 workstation-2 explorer.exe explorer.exe
2024-01-15 09:30:00 workstation-2 notepad.exe explorer.exe

Example 11: Filtering by rank (top-n per group)

Goal: Find the top 3 events by bytes sent within each event type. XQL code:

dataset = xdr_data
| windowcomp rank() by event_type sort desc bytes_sent as bytes_rank
| filter bytes_rank <= 3
| fields event_type, bytes_sent, bytes_rank

Explanation: The rank() function ranks events within each event_type partition by bytes_sent in descending order. The subsequent filter stage keeps only the top 3 ranked events per group. This pattern is commonly used for top-N analysis. Output:

EVENT_TYPE BYTES_SENT BYTES_RANK
NETWORK 5000 1
NETWORK 3200 2
NETWORK 2800 3
PROCESS 1500 1
PROCESS 1200 2
PROCESS 800 3