diff --git a/apl/apl-features.mdx b/apl/apl-features.mdx index 4821e95c..3e33843b 100644 --- a/apl/apl-features.mdx +++ b/apl/apl-features.mdx @@ -127,7 +127,7 @@ keywords: ['axiom documentation', 'documentation', 'axiom', 'APL', 'axiom proces | IP function | [ipv6_is_match](/apl/scalar-functions/ip-functions/ipv6-is-match) | Checks if IPv6 matches pattern. | | IP function | [parse_ipv4_mask](/apl/scalar-functions/ip-functions/parse-ipv4-mask) | Converts IPv4 and mask to long integer. | | IP function | [parse_ipv4](/apl/scalar-functions/ip-functions/parse-ipv4) | Converts IPv4 to long integer. | -| Logical operator | [!=](/apl/scalar-operators/logical-operators) | Returns `true` if either one (or both) of the operands are null, or they are not equal to each other. Otherwise, `false`. | +| Logical operator | [!=](/apl/scalar-operators/logical-operators) | Returns `true` if either one (or both) of the operands are null, or they aren’t equal to each other. Otherwise, `false`. | | Logical operator | [==](/apl/scalar-operators/logical-operators) | Returns `true` if both operands are non-null and equal to each other. Otherwise, `false`. | | Logical operator | [and](/apl/scalar-operators/logical-operators) | Returns `true` if both operands are `true`. | | Logical operator | [or](/apl/scalar-operators/logical-operators) | Returns `true` if one of the operands is `true`, regardless of the other operand. | @@ -298,6 +298,11 @@ keywords: ['axiom documentation', 'documentation', 'axiom', 'APL', 'axiom proces | Time series function | [series_acos](/apl/scalar-functions/time-series/series-acos) | Returns the inverse cosine (arccos) of a series. | | Time series function | [series_asin](/apl/scalar-functions/time-series/series-asin) | Returns the inverse sine (arcsin) of a series. | | Time series function | [series_atan](/apl/scalar-functions/time-series/series-atan) | Returns the inverse tangent (arctan) of a series. | +| Time series function | [series_greater](/apl/scalar-functions/time-series/series-greater) | Returns the elements of a series that are greater than a specified value. | +| Time series function | [series_greater_equals](/apl/scalar-functions/time-series/series-greater-equals) | Returns the elements of a series that are greater than or equal to a specified value. | +| Time series function | [series_less](/apl/scalar-functions/time-series/series-less) | Returns the elements of a series that are less than a specified value. | +| Time series function | [series_less_equals](/apl/scalar-functions/time-series/series-less-equals) | Returns the elements of a series that are less than or equal to a specified value. | +| Time series function | [series_not_equals](/apl/scalar-functions/time-series/series-not-equals) | Returns the elements of a series that aren’t equal to a specified value. | | Type function | [iscc](/apl/scalar-functions/type-functions/iscc) | Checks whether a value is a valid credit card (CC) number. | | Type function | [isimei](/apl/scalar-functions/type-functions/isimei) | Checks whether a value is a valid International Mobile Equipment Identity (IMEI) number. | | Type function | [ismap](/apl/scalar-functions/type-functions/ismap) | Checks whether a value is of the `dynamic` type and represents a mapping. | diff --git a/apl/data-types/map-fields.mdx b/apl/data-types/map-fields.mdx index 2ceb227d..eb558659 100644 --- a/apl/data-types/map-fields.mdx +++ b/apl/data-types/map-fields.mdx @@ -135,7 +135,7 @@ For example, consider the following: Axiom treats the flattened fields (`['geo.city']` and `['geo.country']`) and the subfields of the map field (`['geo']['city']` and `['geo']['country']`) as separate fields and doesn’t maintain a relationship between them. -Queries using `['geo.city']` access a field literally named `geo.city`, while `['geo']['city']` accesses the `city` key inside a `geo` map. These references are not equivalent. +Queries using `['geo.city']` access a field literally named `geo.city`, while `['geo']['city']` accesses the `city` key inside a `geo` map. These references aren’t equivalent. To avoid confusion: diff --git a/apl/scalar-functions/conversion-functions.mdx b/apl/scalar-functions/conversion-functions.mdx index c0e20aff..f9be7df0 100644 --- a/apl/scalar-functions/conversion-functions.mdx +++ b/apl/scalar-functions/conversion-functions.mdx @@ -240,7 +240,7 @@ Converts input to a hexadecimal string. ### Arguments -- Expr: int or long value that will be converted to a hex string. Other types are not supported. +- Expr: int or long value that will be converted to a hex string. Other types aren’t supported. ### Returns diff --git a/apl/scalar-functions/mathematical-functions/set-has-element.mdx b/apl/scalar-functions/mathematical-functions/set-has-element.mdx index fd49e84d..14c4d5fd 100644 --- a/apl/scalar-functions/mathematical-functions/set-has-element.mdx +++ b/apl/scalar-functions/mathematical-functions/set-has-element.mdx @@ -95,5 +95,5 @@ Use `set_has_element` to determine if a set contains a specific value. ## List of related functions -- [set_difference](apl/scalar-functions/mathematical-functions/set-difference): Returns elements in the first array that are not in the second. Use it to find exclusions. +- [set_difference](apl/scalar-functions/mathematical-functions/set-difference): Returns elements in the first array that aren’t in the second. Use it to find exclusions. - [set_union](/apl/scalar-functions/mathematical-functions/set-union): Returns the union of two or more sets. Use it when you need any element that appears in at least one set instead of every set. \ No newline at end of file diff --git a/apl/scalar-functions/mathematical-functions/set-intersect.mdx b/apl/scalar-functions/mathematical-functions/set-intersect.mdx index 130ef58d..adc8cb7f 100644 --- a/apl/scalar-functions/mathematical-functions/set-intersect.mdx +++ b/apl/scalar-functions/mathematical-functions/set-intersect.mdx @@ -95,6 +95,6 @@ Use `set_intersect` to return the intersection of two arrays. ## List of related functions -- [set_difference](apl/scalar-functions/mathematical-functions/set-difference): Returns elements in the first array that are not in the second. Use it to find exclusions. +- [set_difference](apl/scalar-functions/mathematical-functions/set-difference): Returns elements in the first array that aren’t in the second. Use it to find exclusions. - [set_has_element](/apl/scalar-functions/mathematical-functions/set-has-element): Tests whether a set contains a specific value. Prefer it when you only need a Boolean result. - [set_union](/apl/scalar-functions/mathematical-functions/set-union): Returns the union of two or more sets. Use it when you need any element that appears in at least one set instead of every set. diff --git a/apl/scalar-functions/mathematical-functions/set-union.mdx b/apl/scalar-functions/mathematical-functions/set-union.mdx index 7fa620c0..efc1f5b7 100644 --- a/apl/scalar-functions/mathematical-functions/set-union.mdx +++ b/apl/scalar-functions/mathematical-functions/set-union.mdx @@ -91,6 +91,6 @@ Use `set_union` to return the union of two arrays. ## List of related functions -- [set_difference](apl/scalar-functions/mathematical-functions/set-difference): Returns elements in the first array that are not in the second. Use it to find exclusions. +- [set_difference](apl/scalar-functions/mathematical-functions/set-difference): Returns elements in the first array that aren’t in the second. Use it to find exclusions. - [set_has_element](/apl/scalar-functions/mathematical-functions/set-has-element): Tests whether a set contains a specific value. Prefer it when you only need a Boolean result. - [set_union](/apl/scalar-functions/mathematical-functions/set-union): Returns the union of two or more sets. Use it when you need any element that appears in at least one set instead of every set. \ No newline at end of file diff --git a/apl/scalar-functions/time-series/overview.mdx b/apl/scalar-functions/time-series/overview.mdx index 4fffa2cb..ef2c7920 100644 --- a/apl/scalar-functions/time-series/overview.mdx +++ b/apl/scalar-functions/time-series/overview.mdx @@ -12,3 +12,8 @@ The table summarizes the time series functions available in APL. | [series_acos](/apl/scalar-functions/time-series/series-acos) | Returns the inverse cosine (arccos) of a series. | | [series_asin](/apl/scalar-functions/time-series/series-asin) | Returns the inverse sine (arcsin) of a series. | | [series_atan](/apl/scalar-functions/time-series/series-atan) | Returns the inverse tangent (arctan) of a series. | +| [series_greater](/apl/scalar-functions/time-series/series-greater) | Returns the elements of a series that are greater than a specified value. | +| [series_greater_equals](/apl/scalar-functions/time-series/series-greater-equals) | Returns the elements of a series that are greater than or equal to a specified value. | +| [series_less](/apl/scalar-functions/time-series/series-less) | Returns the elements of a series that are less than a specified value. | +| [series_less_equals](/apl/scalar-functions/time-series/series-less-equals) | Returns the elements of a series that are less than or equal to a specified value. | +| [series_not_equals](/apl/scalar-functions/time-series/series-not-equals) | Returns the elements of a series that aren’t equal to a specified value. | diff --git a/apl/scalar-functions/time-series/series-greater-equals.mdx b/apl/scalar-functions/time-series/series-greater-equals.mdx new file mode 100644 index 00000000..7eeedc4b --- /dev/null +++ b/apl/scalar-functions/time-series/series-greater-equals.mdx @@ -0,0 +1,156 @@ +--- +title: series_greater_equals +description: 'This page explains how to use the series_greater_equals function in APL.' +--- + +The `series_greater_equals` function compares two numeric arrays element by element and returns a new array of Boolean values. Each element in the result is `true` if the corresponding element in the first array is greater than or equal to the corresponding element in the second array, and `false` otherwise. + +You use this function when you want to perform threshold comparisons across two series of values, such as checking performance metrics against baselines, comparing observed values to expected ranges, or evaluating time-aligned logs and traces. + +## For users of other query languages + +If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL. + + + + +In Splunk SPL, you typically perform comparisons on fields or with `eval` expressions rather than array-based functions. If you want to compare series of values, you usually use `eval` with conditional expressions, but SPL doesn’t provide direct array-to-array comparison. In APL, `series_greater_equals` lets you apply the comparison element by element on arrays. + + +```sql Splunk example +... | eval greater_equals = if(field1 >= field2, true(), false()) +```` + +```kusto APL equivalent +print result = series_greater_equals(dynamic([2,4,6]), dynamic([1,4,10])) +``` + + + + + + +ANSI SQL does not natively support array-to-array operations in the same way. You often need to `UNNEST` arrays or join on row numbers to compare values across two arrays. APL provides a direct function, `series_greater_equals`, that simplifies these operations by applying the comparison across the entire array at once. + + +```sql SQL example +-- SQL-style comparison would require unnesting +SELECT a.value >= b.value AS greater_equals +FROM UNNEST(ARRAY[2,4,6]) WITH ORDINALITY a(value, i) +JOIN UNNEST(ARRAY[1,4,10]) WITH ORDINALITY b(value, j) + ON a.i = b.j +``` + +```kusto APL equivalent +print result = series_greater_equals(dynamic([2,4,6]), dynamic([1,4,10])) +``` + + + + + + +## Usage + +### Syntax + +```kusto +series_greater_equals(array1, array2) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | --------------------------------- | ------------------------------------------------------------ | +| `array1` | dynamic (array of numeric values) | The first input array. | +| `array2` | dynamic (array of numeric values) | The second input array. Must be the same length as `array1`. | + +### Returns + +A dynamic array of Boolean values where each element is `true` if `array1[i] >= array2[i]`, and `false` otherwise. + +## Use case examples + + + + +In log analysis, you can compare observed request durations against a threshold series to identify requests that are slower than expected. + +**Query** + +```kusto +['sample-http-logs'] +| summarize durations = make_list(req_duration_ms) by id +| extend threshold = dynamic([100,100,100]) +| extend exceeds = series_greater_equals(durations, threshold) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20durations%20%3D%20make_list(req_duration_ms)%20by%20id%20%7C%20extend%20threshold%20%3D%20dynamic(%5B100%2C100%2C100%5D)%20%7C%20extend%20exceeds%20%3D%20series_greater_equals(durations%2C%20threshold)%22%7D) + +**Output** + +| id | durations | threshold | exceeds | +| ---- | ------------- | -------------- | ------------------ | +| u123 | [120,80,150] | [100,100,100] | [true,false,true] | + +This query groups request durations by user ID, builds a list of durations, and checks each against the threshold series of 100 ms. + + + + +In OpenTelemetry traces, you can compare span durations from one service with expected baselines to detect performance regressions. + +**Query** + +```kusto +['otel-demo-traces'] +| where ['service.name'] == 'checkout' +| summarize durations = make_list(duration) by trace_id +| extend baseline = dynamic([100ms,200ms,300ms]) +| extend slower = series_greater_equals(durations, baseline) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20where%20%5B'service.name'%5D%20%3D%3D%20'checkout'%20%7C%20summarize%20durations%20%3D%20make_list(duration)%20by%20trace_id%20%7C%20extend%20baseline%20%3D%20dynamic(%5B100ms%2C200ms%2C300ms%5D)%20%7C%20extend%20slower%20%3D%20series_greater_equals(durations%2C%20baseline)%22%7D) + +**Output** + +| trace_id | durations | baseline | slower | +| --------- | -------------------- | -------------------- | ------------------ | +| t001 | [120ms,180ms,400ms] | [100ms,200ms,300ms] | [true,false,true] | + +This query checks if spans in the checkout service are slower than the defined baseline series. + + + + +In security logs, you can compare the frequency of failed status codes against a threshold to detect suspicious behavior. + +**Query** + +```kusto +['sample-http-logs'] +| where status == '500' +| summarize fails = make_list(req_duration_ms) by ['geo.country'] +| extend threshold = dynamic([200,200,200]) +| extend suspicious = series_greater_equals(fails, threshold) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20where%20status%20%3D%3D%20'500'%20%7C%20summarize%20fails%20%3D%20make_list(req_duration_ms)%20by%20%5B'geo.country'%5D%20%7C%20extend%20threshold%20%3D%20dynamic(%5B200%2C200%2C200%5D)%20%7C%20extend%20suspicious%20%3D%20series_greater_equals(fails%2C%20threshold)%22%7D) + +**Output** + +| geo.country | fails | threshold | suspicious | +| ----------- | -------------- | -------------- | ------------------ | +| US | [210,190,300] | [200,200,200] | [true,false,true] | + +This query aggregates failed requests by country, builds a series of durations, and compares them against a 200 ms threshold to highlight suspiciously slow failures. + + + + +## List of related functions + +- [series_greater](/apl/scalar-functions/time-series/series-greater): Compares two arrays and returns `true` where the first array element is greater than the second. +- [series_less](/apl/scalar-functions/time-series/series-less): Compares two arrays and returns `true` where the first array element is less than the second. +- [series_less_equals](/apl/scalar-functions/time-series/series-less-equals): Compares two arrays and returns `true` where the first array element is less than or equal to the second. +- [series_not_equals](/apl/scalar-functions/time-series/series-not-equals): Compares two arrays and returns `true` where elements aren’t equal. diff --git a/apl/scalar-functions/time-series/series-greater.mdx b/apl/scalar-functions/time-series/series-greater.mdx new file mode 100644 index 00000000..6e4d162a --- /dev/null +++ b/apl/scalar-functions/time-series/series-greater.mdx @@ -0,0 +1,132 @@ +--- +title: series_greater +description: 'This page explains how to use the series_greater function in APL.' +--- + +The `series_greater` function compares two numeric arrays (series) element by element and returns a new array of Boolean values. Each element in the result is `true` if the corresponding element in the first array is greater than the corresponding element in the second array, and `false` otherwise. + +You use this function when you want to evaluate pairwise comparisons across time series or numeric arrays. It’s especially useful in scenarios such as anomaly detection, trend analysis, or validating thresholds against observed metrics. + +## For users of other query languages + +If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL. + + + + +In Splunk SPL, comparisons are usually done across fields or using the `eval` command with conditional expressions. There is no direct equivalent to element-by-element array comparisons. In APL, `series_greater` performs this comparison across arrays in a single function call. + + +```sql Splunk example +... | eval comparison = if(fieldA > fieldB, true(), false()) +```` + +```kusto APL equivalent +print result = series_greater(dynamic([1,2,3]), dynamic([2,2,2])) +``` + + + + + + +In ANSI SQL, comparisons are scalar and operate on single values at a time. You usually need to use `CASE` statements for conditionals. SQL lacks a built-in function for element-wise array comparison. In APL, `series_greater` directly compares two arrays and returns an array of Boolean values. + + +```sql SQL example +SELECT CASE WHEN a > b THEN TRUE ELSE FALSE END as comparison +FROM numbers +``` + +```kusto APL equivalent +print result = series_greater(dynamic([10,20,30]), dynamic([15,10,30])) +``` + + + + + + +## Usage + +### Syntax + +```kusto +series_greater(array1, array2) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | --------------- | ----------------------------------------------------------------- | +| `array1` | dynamic (array) | The first array to compare. | +| `array2` | dynamic (array) | The second array to compare. Must be the same length as `array1`. | + +### Returns + +A dynamic array of Boolean values, where each element is `true` if the corresponding element in `array1` is greater than the corresponding element in `array2`, and `false` otherwise. + +## Use case examples + + + + +When analyzing HTTP request durations, you can compare them against a fixed threshold to identify requests that exceed performance expectations. + +**Query** + +```kusto +['sample-http-logs'] +| summarize durations = make_list(req_duration_ms) by id +| extend threshold = dynamic([200,200,200,200]) +| extend above_threshold = series_greater(durations, threshold) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20durations%20%3D%20make_list(req_duration_ms)%20by%20id%20%7C%20extend%20threshold%20%3D%20dynamic(%5B200%2C200%2C200%2C200%5D)%20%7C%20extend%20above_threshold%20%3D%20series_greater(durations%2C%20threshold)%22%7D) + +**Output** + +| id | durations | threshold | above_threshold | +| ---- | ------------------ | ------------------ | ------------------------ | +| u123 | [180,220,150,300] | [200,200,200,200] | [false,true,false,true] | + +This query shows which requests for a given user exceed a threshold of 200 ms. + + + + +You can compare span durations across services to see where certain spans take longer than others. + +**Query** + +```kusto +['otel-demo-traces'] +| where ['service.name'] == 'frontend' +| summarize frontend_spans = make_list(duration) by trace_id +| join kind=inner ( + ['otel-demo-traces'] + | where ['service.name'] == 'checkout' + | summarize checkout_spans = make_list(duration) by trace_id +) on trace_id +| extend longer_in_frontend = series_greater(frontend_spans, checkout_spans) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20where%20%5B'service.name'%5D%20%3D%3D%20'frontend'%20%7C%20summarize%20frontend_spans%20%3D%20make_list(duration)%20by%20trace_id%20%7C%20join%20kind%3Dinner%20(%20%5B'otel-demo-traces'%5D%20%7C%20where%20%5B'service.name'%5D%20%3D%3D%20'checkout'%20%7C%20summarize%20checkout_spans%20%3D%20make_list(duration)%20by%20trace_id%20)%20on%20trace_id%20%7C%20extend%20longer_in_frontend%20%3D%20series_greater(frontend_spans%2C%20checkout_spans)%22%7D) + +**Output** + +| trace_id | frontend_spans | checkout_spans | longer_in_frontend | +| --------- | ----------------- | ----------------- | -------------------- | +| t1 | [30ms,50ms,10ms] | [20ms,40ms,15ms] | [true,true,false] | + +This query compares span durations between `frontend` and `checkoutservice` services. + + + + +## List of related functions + +- [series_greater_equals](/apl/scalar-functions/time-series/series-greater-equals): Compares two arrays and returns `true` when elements in the first array are greater than or equal to the second array. +- [series_less](/apl/scalar-functions/time-series/series-less): Compares two arrays and returns `true` where the first array element is less than the second. +- [series_less_equals](/apl/scalar-functions/time-series/series-less-equals): Compares two arrays and returns `true` where the first array element is less than or equal to the second. +- [series_not_equals](/apl/scalar-functions/time-series/series-not-equals): Compares two arrays and returns `true` where elements aren’t equal. diff --git a/apl/scalar-functions/time-series/series-less-equals.mdx b/apl/scalar-functions/time-series/series-less-equals.mdx new file mode 100644 index 00000000..c6397712 --- /dev/null +++ b/apl/scalar-functions/time-series/series-less-equals.mdx @@ -0,0 +1,156 @@ +--- +title: series_less_equals +description: 'This page explains how to use the series_less_equals function in APL.' +--- + +The `series_less_equals` function compares two numeric arrays element by element and returns a new array of Boolean values. Each element in the result is `true` if the corresponding element in the first array is less than or equal to the corresponding element in the second array, and `false` otherwise. + +You can use this function to analyze numeric sequences over time, such as detecting when one series of measurements stays below or matches another. This is useful in monitoring scenarios, anomaly detection, and when working with time-series data in logs, traces, or security events. + +## For users of other query languages + +If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL. + + + + +In Splunk SPL, comparisons across arrays aren’t directly supported in the same way. SPL typically works with single values or requires custom evaluation functions to iterate over arrays. In APL, `series_less_equals` provides a built-in way to compare arrays element by element. + + +```sql Splunk example +| eval result=if(value1 <= value2, true(), false()) +```` + +```kusto APL equivalent +print arr1=dynamic([1,2,3]), arr2=dynamic([2,2,2]) +| extend result=series_less_equals(arr1, arr2) +``` + + + + + + +In ANSI SQL, comparisons are scalar by default. You cannot compare arrays directly without unnesting or joining them. In APL, `series_less_equals` lets you perform an element-wise comparison of two arrays with a single function call. + + +```sql SQL example +SELECT CASE WHEN a.value <= b.value THEN true ELSE false END +FROM array_table_a a +JOIN array_table_b b ON a.idx = b.idx; +``` + +```kusto APL equivalent +print arr1=dynamic([1,2,3]), arr2=dynamic([2,2,2]) +| extend result=series_less_equals(arr1, arr2) +``` + + + + + + +## Usage + +### Syntax + +```kusto +series_less_equals(arr1, arr2) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | --------------- | -------------------------------------------------------------- | +| `arr1` | dynamic (array) | The first numeric array. | +| `arr2` | dynamic (array) | The second numeric array. Must have the same length as `arr1`. | + +### Returns + +A dynamic array of Boolean values. Each element is `true` if the element of `arr1` is less than or equal to the corresponding element of `arr2`, otherwise `false`. + +## Use case examples + + + + +You want to check whether request durations for a user stay within an acceptable threshold over time. + +**Query** + +```kusto +['sample-http-logs'] +| summarize durations=make_list(req_duration_ms), times=make_list(_time) by id +| extend threshold=dynamic([200, 200, 200]) +| extend below_or_equal=series_less_equals(durations, threshold) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20durations%3Dmake_list(req_duration_ms)%2C%20times%3Dmake_list(_time)%20by%20id%20%7C%20extend%20threshold%3Ddynamic(%5B200%2C%20200%2C%20200%5D)%20%7C%20extend%20below_or_equal%3Dseries_less_equals(durations%2C%20threshold)%22%7D) + +**Output** + +| id | durations | threshold | below_or_equal | +| -- | ---------------- | ---------------- | -------------------- | +| u1 | [120, 180, 250] | [200, 200, 200] | [true, true, false] | + +This query checks for each user whether the request duration at each point is less than or equal to the threshold of 200 ms. + + + + +You want to validate whether service durations stay within a performance baseline. + +**Query** + +```kusto +['otel-demo-traces'] +| where ['service.name'] == 'frontend' +| summarize durations=make_list(duration), times=make_list(_time) by trace_id +| extend baseline=dynamic([1000000000, 1000000000, 1000000000]) +| extend below_or_equal=series_less_equals(durations, baseline) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20where%20%5B'service.name'%5D%20%3D%3D%20'frontend'%20%7C%20summarize%20durations%3Dmake_list(duration)%2C%20times%3Dmake_list(_time)%20by%20trace_id%20%7C%20extend%20baseline%3Ddynamic(%5B1000000000%2C%201000000000%2C%201000000000%5D)%20%7C%20extend%20below_or_equal%3Dseries_less_equals(durations%2C%20baseline)%22%7D) + +**Output** + +| trace_id | durations | baseline | below_or_equal | +| --------- | ------------------------- | --------------------- | ---------------- | +| t1 | [00:00:00.5, 00:00:01.2] | [00:00:01, 00:00:01] | [true, false] | + +This query shows whether spans in the frontend service meet a performance baseline of 1 second. + + + + +You want to check whether requests from a given country stay within acceptable request duration limits. + +**Query** + +```kusto +['sample-http-logs'] +| where ['geo.country'] == 'United States' +| summarize durations=make_list(req_duration_ms), times=make_list(_time) by id +| extend limits=dynamic([300, 300, 300]) +| extend below_or_equal=series_less_equals(durations, limits) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20where%20%5B'geo.country'%5D%20%3D%3D%20'United%20States'%20%7C%20summarize%20durations%3Dmake_list(req_duration_ms)%2C%20times%3Dmake_list(_time)%20by%20id%20%7C%20extend%20limits%3Ddynamic(%5B300%2C%20300%2C%20300%5D)%20%7C%20extend%20below_or_equal%3Dseries_less_equals(durations%2C%20limits)%22%7D) + +**Output** + +| id | durations | limit | below_or_equal | +| -- | ---------------- | ---------------- | -------------------- | +| u2 | [220, 280, 350] | [300, 300, 300] | [true, true, false] | + +This query checks whether requests originating in the United States remain within a 300 ms duration limit. + + + + +## List of related functions + +- [series_greater_equals](/apl/scalar-functions/time-series/series-greater-equals): Compares two arrays and returns `true` when elements in the first array are greater than or equal to the second array. +- [series_greater](/apl/scalar-functions/time-series/series-greater): Compares two arrays and returns `true` where the first array element is greater than the second. +- [series_less](/apl/scalar-functions/time-series/series-less): Compares two arrays and returns `true` where the first array element is less than the second. +- [series_not_equals](/apl/scalar-functions/time-series/series-not-equals): Compares two arrays and returns `true` where elements aren’t equal. diff --git a/apl/scalar-functions/time-series/series-less.mdx b/apl/scalar-functions/time-series/series-less.mdx new file mode 100644 index 00000000..38da6fa3 --- /dev/null +++ b/apl/scalar-functions/time-series/series-less.mdx @@ -0,0 +1,160 @@ +--- +title: series_less +description: 'This page explains how to use the series_less function in APL.' +--- + +The `series_less` function compares two numeric arrays element by element and returns a Boolean array. Each position in the result contains `true` if the element in the first array is less than the corresponding element in the second array, and `false` otherwise. + +You use `series_less` when you want to evaluate trends across sequences of numeric values. It’s especially useful in time series analysis, anomaly detection, or comparing metrics side by side. For example, you can check if response times are decreasing compared to a baseline or if one service consistently performs faster than another. + +## For users of other query languages + +If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL. + + + + +In Splunk SPL, comparisons across series typically rely on `eval` with conditional expressions or custom logic in combination with `timechart`. In contrast, APL provides specialized `series_*` functions like `series_less` to directly compare arrays element by element. + + +```sql Splunk example +... | timechart avg(req_duration_ms) as avg_dur +| eval faster = if(avg_dur < 200, true, false) +```` + +```kusto APL equivalent +['sample-http-logs'] +| make-series avg(req_duration_ms) on _time step 1m +| extend is_less = series_less(avg_req_duration_ms, array_concat(dynamic([200]))) +``` + + + + + + +In ANSI SQL, you normally compare scalar values rather than arrays. To compare sequences, you need to join tables with offsets or use window functions. In APL, `series_less` simplifies this by applying the comparison across arrays in a single step. + + +```sql SQL example +SELECT t1._time, + CASE WHEN t1.req_duration_ms < t2.req_duration_ms THEN true ELSE false END AS is_less +FROM logs t1 +JOIN logs t2 + ON t1._time = t2._time +``` + +```kusto APL equivalent +['sample-http-logs'] +| make-series avg(req_duration_ms) on _time step 1m +| extend compare = series_less(avg_req_duration_ms, avg_req_duration_ms[1:]) +``` + + + + + + +## Usage + +### Syntax + +```kusto +series_less(array1, array2) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | ----- | -------------------------------------------------------------------------- | +| `array1` | array | The first array of numeric values. | +| `array2` | array | The second array of numeric values. Must have the same length as `array1`. | + +### Returns + +An array of Boolean values. Each element is `true` if the corresponding element in `array1` is less than the element in `array2`, otherwise `false`. + +## Use case examples + + + + +You want to check whether the average request duration in each city is less than a fixed threshold of 150 milliseconds. + +**Query** + +```kusto +['sample-http-logs'] | take 50 | make-series city_avg = avg(req_duration_ms) on _time step 1h by ['geo.city'] | extend threshold = dynamic([150, 150, 150]) | extend is_less = series_less(city_avg, threshold) +```` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20take%2050%20%7C%20make-series%20city_avg%20%3D%20avg(req_duration_ms)%20on%20_time%20step%201h%20by%20%5B'geo.city'%5D%20%7C%20extend%20threshold%20%3D%20dynamic(%5B150%2C%20150%2C%20150%5D)%20%7C%20extend%20is_less%20%3D%20series_less(city_avg%2C%20threshold)%22%7D) + +**Output** + +| geo.city | city_avg | threshold | is_less | +| -------- | ---------------- | ---------------- | ---------------------- | +| London | [120, 90, 100] | [150, 150, 150] | [true, true, true] | +| Paris | [180, 200, 190] | [150, 150, 150] | [false, false, false] | + +This query shows whether each city’s request duration stays below a 150 ms threshold at each time step. + + + + +You want to verify if the duration of each span is less than the corresponding duration of the previous span for the same service. + +**Query** + +```kusto +['otel-demo-traces'] +| make-series span_durations = avg(duration) on _time step 5m by ['service.name'] +| extend shifted = series_shift(span_durations, 1) +| extend decreasing = series_less(span_durations, shifted) +``` + +[Run in Playground]([https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22['otel-demo-traces](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22['otel-demo-traces)'] | make-series span_durations = avg(duration) on _time step 5m by ['service.name'] | extend shifted = series_shift(span_durations, 1) | extend decreasing = series_less(span_durations, shifted)%22%7D) + +**Output** + +| service.name | span_durations | shifted | decreasing | +| ------------ | -------------------- | -------------------- | -------------------- | +| frontend | [50ms, 40ms, 45ms] | [null, 50ms, 40ms] | [null, true, false] | +| cartservice | [100ms, 95ms, 90ms] | [null, 100ms, 95ms] | [null, true, true] | + +This query identifies whether spans are becoming shorter compared to the previous time bucket. + + + + +You want to detect if failed requests in each country are consistently less than successful requests. + +**Query** + +```kusto +['sample-http-logs'] +| take 50 +| summarize success = countif(status == '200'), failure = countif(status != '200') by ['geo.country'], bin(_time, 1h) +| make-series success_series = avg(success), failure_series = avg(failure) on _time step 1h by ['geo.country'] +| extend failures_less = series_less(failure_series, success_series) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20take%2050%20%7C%20summarize%20success%20%3D%20countif(status%20%3D%3D%20'200')%2C%20failure%20%3D%20countif(status%20!%3D%20'200')%20by%20%5B'geo.country'%5D%2C%20bin(_time%2C%201h)%20%7C%20make-series%20success_series%20%3D%20avg(success)%2C%20failure_series%20%3D%20avg(failure)%20on%20_time%20step%201h%20by%20%5B'geo.country'%5D%20%7C%20extend%20failures_less%20%3D%20series_less(failure_series%2C%20success_series)%22%7D) + +**Output** + +| geo.country | success_series | failure_series | failures_less | +| ----------- | ---------------- | --------------- | ------------------- | +| US | [300, 280, 310] | [10, 20, 15] | [true, true, true] | +| UK | [150, 140, 160] | [20, 25, 30] | [true, true, true] | + +This query checks whether failures stay consistently lower than successful requests across time intervals. + + + + +## List of related functions + +- [series_greater_equals](/apl/scalar-functions/time-series/series-greater-equals): Compares two arrays and returns `true` when elements in the first array are greater than or equal to the second array. +- [series_greater](/apl/scalar-functions/time-series/series-greater): Compares two arrays and returns `true` where the first array element is greater than the second. +- [series_less_equals](/apl/scalar-functions/time-series/series-less-equals): Compares two arrays and returns `true` where the first array element is less than or equal to the second. +- [series_not_equals](/apl/scalar-functions/time-series/series-not-equals): Compares two arrays and returns `true` where elements aren’t equal. diff --git a/apl/scalar-functions/time-series/series-not-equals.mdx b/apl/scalar-functions/time-series/series-not-equals.mdx new file mode 100644 index 00000000..5a7b4e99 --- /dev/null +++ b/apl/scalar-functions/time-series/series-not-equals.mdx @@ -0,0 +1,167 @@ +--- +title: series_not_equals +description: 'This page explains how to use the series_not_equals function in APL.' +--- + +The `series_not_equals` function compares two numeric arrays element by element and returns a new array of Boolean values. Each element in the output array indicates whether the corresponding elements in the input arrays aren’t equal. + +You use this function when you want to detect differences between two time series or arrays of values. It’s particularly useful when analyzing request patterns, response times, or service traces, where identifying mismatches across parallel series matters. + +## For users of other query languages + +If you come from other query languages, this section explains how to adjust your existing queries to achieve the same results in APL. + + + + +In Splunk SPL, you typically compare fields directly with the `!=` operator. In APL, `series_not_equals` applies this logic to arrays, returning an array of Boolean values instead of a single Boolean. + + +```sql Splunk example +... | eval is_different = if(fieldA != fieldB, 1, 0) +```` + +```kusto APL equivalent +print result = series_not_equals(dynamic([1,2,3]), dynamic([1,5,3])) +``` + + + + + + +In ANSI SQL, comparisons with `<>` return a single Boolean for each row. APL’s `series_not_equals` function extends this idea to arrays, producing a series of Boolean values instead of a single Boolean. + + +```sql SQL example +SELECT fieldA <> fieldB AS is_different +FROM my_table +``` + +```kusto APL equivalent +print result = series_not_equals(dynamic([10,20,30]), dynamic([10,25,30])) +``` + + + + + + +## Usage + +### Syntax + +```kusto +series_not_equals(series1, series2) +``` + +### Parameters + +| Parameter | Type | Description | +| --------- | --------------- | ---------------------------------------------------------------------------- | +| `series1` | dynamic (array) | The first numeric array to compare. | +| `series2` | dynamic (array) | The second numeric array to compare. Must have the same length as `series1`. | + +### Returns + +A dynamic array of Boolean values. Each element is `true` if the corresponding elements in the input arrays aren’t equal, and `false` otherwise. + +## Use case examples + + + + +You can use `series_not_equals` to identify differences in request durations across two groups of HTTP requests. + +**Query** + +```kusto +['sample-http-logs'] +| summarize durations1 = make_list(req_duration_ms) by method +| join ( + ['sample-http-logs'] + | summarize durations2 = make_list(req_duration_ms) by method +) on method +| extend diff_flags = series_not_equals(durations1, durations2) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20summarize%20durations1%20%3D%20make_list(req_duration_ms)%20by%20method%20%7C%20join%20(%20%5B'sample-http-logs'%5D%20%7C%20summarize%20durations2%20%3D%20make_list(req_duration_ms)%20by%20method%20)%20on%20method%20%7C%20extend%20diff_flags%20%3D%20series_not_equals(durations1%2C%20durations2)%22%7D) + +**Output** + +| method | diff_flags | +| ------ | ------------------- | +| GET | [false,true,false] | +| POST | [true,false,true] | + +This query builds two lists of request durations grouped by method, compares them element by element, and returns an array showing where values differ. + + + + +You can use `series_not_equals` to compare the duration of spans between two services in the same trace. + +**Query** + +```kusto +['otel-demo-traces'] +| where ['service.name'] == 'frontend' +| summarize frontend_durations = make_list(duration) by trace_id +| join ( + ['otel-demo-traces'] + | where ['service.name'] == 'checkout' + | summarize checkout_durations = make_list(duration) by trace_id +) on trace_id +| extend diff_flags = series_not_equals(frontend_durations, checkout_durations) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'otel-demo-traces'%5D%20%7C%20where%20%5B'service.name'%5D%20%3D%3D%20'frontend'%20%7C%20summarize%20frontend_durations%20%3D%20make_list(duration)%20by%20trace_id%20%7C%20join%20(%20%5B'otel-demo-traces'%5D%20%7C%20where%20%5B'service.name'%5D%20%3D%3D%20'checkout'%20%7C%20summarize%20checkout_durations%20%3D%20make_list(duration)%20by%20trace_id%20)%20on%20trace_id%20%7C%20extend%20diff_flags%20%3D%20series_not_equals(frontend_durations%2C%20checkout_durations)%22%7D) + +**Output** + +| trace_id | diff_flags | +| --------- | ------------- | +| abc123 | [false,true] | +| def456 | [true,false] | + +This query compares span durations between `frontend` and `checkoutservice` for the same trace and shows where durations differ. + + + + +You can use `series_not_equals` to check if HTTP status codes differ between requests from different countries. + +**Query** + +```kusto +['sample-http-logs'] +| where ['geo.country'] == 'United States' +| summarize us_statuses = make_list(status) by uri +| join ( + ['sample-http-logs'] + | where ['geo.country'] == 'Germany' + | summarize de_statuses = make_list(status) by uri +) on uri +| extend diff_flags = series_not_equals(us_statuses, de_statuses) +``` + +[Run in Playground](https://play.axiom.co/axiom-play-qf1k/query?initForm=%7B%22apl%22%3A%22%5B'sample-http-logs'%5D%20%7C%20where%20%5B'geo.country'%5D%20%3D%3D%20'United%20States'%20%7C%20summarize%20us_statuses%20%3D%20make_list(status)%20by%20uri%20%7C%20join%20(%20%5B'sample-http-logs'%5D%20%7C%20where%20%5B'geo.country'%5D%20%3D%3D%20'Germany'%20%7C%20summarize%20de_statuses%20%3D%20make_list(status)%20by%20uri%20)%20on%20uri%20%7C%20extend%20diff_flags%20%3D%20series_not_equals(us_statuses%2C%20de_statuses)%22%7D) + +**Output** + +| uri | diff_flags | +| ------------- | ------------------- | +| /api/login | [false,true,false] | +| /api/products | [true,false] | + +This query identifies differences in status codes returned by the same URI when accessed from the US and Germany. + + + + +## List of related functions + +- [series_greater_equals](/apl/scalar-functions/time-series/series-greater-equals): Compares two arrays and returns `true` when elements in the first array are greater than or equal to the second array. +- [series_greater](/apl/scalar-functions/time-series/series-greater): Compares two arrays and returns `true` where the first array element is greater than the second. +- [series_less](/apl/scalar-functions/time-series/series-less): Compares two arrays and returns `true` where the first array element is less than the second. +- [series_less_equals](/apl/scalar-functions/time-series/series-less-equals): Compares two arrays and returns `true` where the first array element is less than or equal to the second. diff --git a/docs.json b/docs.json index 9ba9f45b..b4def543 100644 --- a/docs.json +++ b/docs.json @@ -378,7 +378,12 @@ "apl/scalar-functions/time-series/series-abs", "apl/scalar-functions/time-series/series-acos", "apl/scalar-functions/time-series/series-asin", - "apl/scalar-functions/time-series/series-atan" + "apl/scalar-functions/time-series/series-atan", + "apl/scalar-functions/time-series/series-greater", + "apl/scalar-functions/time-series/series-greater-equals", + "apl/scalar-functions/time-series/series-less", + "apl/scalar-functions/time-series/series-less-equals", + "apl/scalar-functions/time-series/series-not-equals" ] }, {