Common Misconceptions

When learning CQL, there are several common patterns and assumptions that can lead to confusion or unexpected query results. Understanding these misconceptions early can help you write more effective queries and avoid common pitfalls. This section addresses frequent misunderstandings about how CQL functions operate, how parameters work, and how data flows through the query pipeline.

The following topics cover key areas where users commonly encounter challenges:

  • Sharing Values with Functions: Understanding how CQL operates on event pipelines rather than individual values, and how to properly chain operations together.

  • Function Parameters as Fields: Learning the difference between passing field names and values to function parameters, and when each is appropriate.

  • Function Parameters: Understanding the correct syntax for function parameters and how to structure function calls.

  • Arrays in Function Parameters: Recognizing how arrays are implicitly defined in function parameters and when brackets are required.

Field Names in Events

Field names in events are flexible in that they can be simple words, a quoted string, or compound values where each component of the field name makes up a different part of the original data. This compound structure is used for complex structures that may have been parsed from other sources. For example, the JSON object:

json
{
  "key" : "username",
  "value" : "bobs"
}

May get translated into the individual fields:

fieldvalue
myfield.keyusername
myfield.valuebobs

This can also be applied to compound array types:

fieldvalue
myfield[0].keyusername
myfield[0].valuebobs
myfield[1].keyusername
myfield[1].valuesallyq

These fieldnames can also be created during assignment:

logscale
my.Field..name := "value"

Field Exists or Has Any Value

Because each event in the event stream is independent and there is no defined schema, only some events may have the field you want. To identify and filter the event stream to only contain events that include a specific field, you can perform a wildcard search:

logscale
myFieldname=*

This filters and returns only events that have a value for myFieldname.

Output Fields

Pay attention to where and how different functions return or update the information. Some functions output their value to a field as the result, which then becomes part of each event, typically named for the function (or datatype). For example, format() outputs to the _format field by default:

logscale
pidouble := 3.141592654
| format(format="%.2f",field=pidouble)

Generates the field _format with the value 3.14.

These functions support the as parameter to change the output field:

logscale
pidouble := 3.141592654
| format(format="%.2f",field=pidouble,as=pifixed)

Or you can assign to a field:

logscale
pidouble := 3.141592654
| pifixed := format(format="%.2f",field=pidouble)

Parsing During Queries

Parsing data during a query, including parsing or extracting new values or structures for example from a JSON or XML field is perfectly valid during a query operation. This is supported by design because you may only want to extract this information after you have filtered the rest of the content by other fields or values. Extracting it during parsing may be expensive, but extracting only the values you've identified during a query can be a valid way of optimizing your query and extraction.

Similarly, it may be necessary to iterate or process an array or object array and extract different values, or to construct one.

The main consideration is the impact this may have on processing. Performing these operations can be expensive in computational and memory time, so limit the process to only fields after filtering or aggregation.

Queries without Source Data

Trying to run a query, valid or otherwise, without any source event data will not return any values. Trying to execute a query in an empty repository, or on a repository where the time span does not return any events will return nothing. This is true, even if you create fields in the query.

This is because queries are executed based on the stream of events; even creating a new field, needs to create the new field in an event. If the source events are empty, there is no event for the field to be added to.

One solution to this problem is to use the createEvents() function to populate the event stream:

logscale
createEvents("")
| hexvalue := "09AC"

Using the Equals (=) Sign

The use of a single equals sign is a filter for a value, not assignation of a value to a field. So the query:

logscale
myValue = "username"

Filters for the value username in the field myValue.

To assign a value to a field, use :=:

logscale
myValue := "username"

Sharing Values with Functions

The operation for functions and syntax within CQL means that most operations update the incoming list of events, they do not return a single value. This is because CQL operates on the pipeline of events not on the individual value.

For example, to convert a number and then format it into a string, then the following will not work:

Invalid Example for Demonstration - DO NOT USE
logscale
format(format="%,d Megabytes",field=[unit:convert((128*1024, to="M")]])

Instead, each operation must be executed and then the value placed back on the event pipeline:

logscale
rate :- (128*1024)
unit:convert((128*1024, to="M", as=mbrate)
| format("$,d Megabytes",field=mbrate)

This query above:

  1. Calculates the value, placing the value into the rate field.

  2. Converts the value into megabytes, placing the result into the mbrate field.

  3. Formats the number with the thousands separator and a string suffix.

Function Parameters as Fields

Functions typically accept field names and not values or strings to their parameters. For example, the concat() accepts an array of fields to concatenate, not an array of strings. The following query will not concatenate the two strings:

Invalid Example for Demonstration - DO NOT USE
logscale
newstring := concat(["Hello","world"])

Instead, you must assign the values to a field in the event stream and concatenate that:

Invalid Example for Demonstration - DO NOT USE
logscale
string1 := "Hello"
| string2 := "World"
| newstring := concat([string1,string2])

Pay attention to the function description and definition to identify whether a field or value is expected.

Function Parameters

Parameters in LogScale take the format parameter=value, separated by commas. Many functions also support an 'implied' parameter, where using the name of the parameter is entirely optional. For example, the groupBy() has an implied parameter called field, so to group by a field:

logscale
groupBy(username)

Which is equivalent to:

logscale
groupBy(field=username)

The type is also still implied in this situation, so to group by a list of fields:

logscale
groupBy([username,groupname])

When used with other parameters, the implied parameter still does not need to be specified:

logscale
groupBy(status_code, function=count)

Parameter names do not need to be defined in any sequence; there is no order needed. The function call:

logscale
groupBy(field=status_code, function=count)

Is the same as:

logscale
groupBy(function=count, field=status_code)

Arrays in Function Parameters

When a function accepts an array, for example an array of field names, the definition of the value as an array is implicit. This means that each of these is valid:

  • logscale
    groupBy(jobname)
  • logscale
    groupBy([jobname])

Also note that because fieldnames can be quoted, these formats also work:

  • logscale
    groupBy("jobname")
  • logscale
    groupBy(["jobname"])

When referring to the name of an array you must add the [] to the name.