Array Syntax

Falcon LogScale operates on both flat arrays and nested arrays. Within LogScale there is no distinct array type, but LogScale is able to operate on array-like objects using syntax similar to manipulating JSON arrays and objects. Within LogScale, an array is an ordered collection of values: each value is called an element, and each element has a numeric position in the array, known as its index. Nested arrays are fields that consist of an array where each element is another array or object, also known as arrays of objects or arrays of arrays.

As a part of the Crowdstrike Query Language, there is syntax that applies to array functions, for example array:contains(), array:eval() and objectArray:eval() that we refer to as array syntax. Some of the array functions are used on flat arrays, some on nested arrays. The objectArray:eval() function operates on arrays of objects, the nested arrays - note that it can only read the structured array fields, it cannot write to them.

The array functions operate on fields that follow this specific naming conventions syntax.

The array syntax is similar to the one used by JSON, where [ and ] are used for indexing within arrays and . for selecting members in objects. For example:

["jsmith","tmcdonald"]

Is an array with two elements, the first element username[0] has the value jsmith.

While the object:

{username: "jsmith", name: "Jon Smith"}

The username can be extracted using user.username.

The following table compares the JSON and the parsed LogScale form of the same array and object structures. The final column shows the syntax to use when referring to the whole array.

JSON Parsed LogScale event array name syntax
["jsmith","tmcdonald"]

username[0]: "jsmith"

username[1]: "tmcdonald"

username[]
[
  {username: "jsmith", name: "Jon Smith"},
  {username: "jdoe", name: "Jane Doe"}
]

users[0].user.username: "jsmith"

users[0].user.name: "Jon Smith"

users[1].user.username: "jdoe"

users[1].user.name: "Jane Doe"

users[]
[ ["a", "b"], ["c", "d"]]

foo[0][0]: "a"

foo[0][1]: "b"

foo[1][0]: "c"

foo[1][1]: "d"

foo[], foo[0][], and foo[1][]

Structured Array Syntax

Let's start with an example usage:

logscale
objectArray:eval("in[]", asArray="out[]", function={out := format("%s:%s", field=[in.key, in.value])})
input:
       in[0].key = x
       in[0].value = y
       in[1].key = a
       in[1].value = b
output:
       out[0] = x:y
       out[1] = a:b

objectArray:eval("in[]", asArray="out[]", function=function) iterates over the array from start to end (or to the first "hole" in the array).

For each array entry, the given function is applied.

For each entry, at index i, a field out[i] is created if the function writes a value to the output array name field ("out" in this example).

Within the function, a special pattern to access sub-selections of array entries is supported.

This pattern is:

in (the input array) followed by:

  • .<subselection>

  • or [<number>]

  • or any combination of the above

Examples:

  • in.key

  • in.others[0].foo

  • in[0][1]

This pattern is what allows the function to operate on arrays of objects, and arrays of arrays, and other arrays of structured data.

Semantically, given the input array "in", an array index i, and an access in.<subselection> this will be translated to the field name in[i].<subselection>. Similarly, in[2] is translated to in[i][2].

The character sequence could contain another dot. For example in.foo.bar becomes in[i].foo.bar.

At present, it is only possible to read structured array fields. Writing to them is unsupported, but is a possible future extension. For that reason, at present we disallow modifying the existing array.

The var parameter can be used to give a different name to the input array variable inside the function argument. This is particularly useful whenever the input array name is very long. Example:

objectArray:eval("someVeryLongName[]", asArray="out[]", var=x, function={out := format("%s:%s", field=[x.key, x.value])})

Syntax Array Notation

Fields with names consisting of a valid array-prefix followed by an array entry are treated as entries of an array. The indices must be continuously increasing from 0 and upwards.

The entire array is referenced within array function using the array-prefix followed by [] that indicates the entire range of entries, for example username[].

Simplified, the valid array-prefix is a sequence of members and array entries. More formally, a valid array-prefix is either:

  • an identifier — that is, a sequence of alphabetical characters

  • a valid array-prefix followed by a member operator and an identifier — the member operator is a .

  • or a valid array-prefix followed by an array entry — the array entry is an index surrounded by square brackets.

Examples of field names representing entries in an array, and the reference to that array:

Field names following the array syntax How to reference in array functions
a[0], a[1], a[2] a[]
a.b[0], a.b[1], a.b[2] a.b[]
a[0][0], a[0][1] a[0][]

Warning

  • It is only possible to use the objectArray:eval() function across entries containing objects or other arrays, for example, a[][0] and a[].b. None of the other functions can be used.

  • It is only possible to use the objectArray:eval() function across compounded ranges of entries, for example, a[][]. None of the other functions can be used.

  • The objectAarray:eval() function can only read the structured array fields, it cannot write to them.