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:
{
"key" : "username",
"value" : "bobs"
}May get translated into the individual fields:
| field | value |
|---|---|
| myfield.key | username |
| myfield.value | bobs |
This can also be applied to compound array types:
| field | value |
|---|---|
| myfield[0].key | username |
| myfield[0].value | bobs |
| myfield[1].key | username |
| myfield[1].value | sallyq |
These fieldnames can also be created during assignment:
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:
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:
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:
pidouble := 3.141592654
| format(format="%.2f",field=pidouble,as=pifixed)Or you can assign to a field:
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:
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:
myValue = "username"
Filters for the value username in the field
myValue.
To assign a value to a field, use :=:
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:
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:
rate :- (128*1024)
unit:convert((128*1024, to="M", as=mbrate)
| format("$,d Megabytes",field=mbrate)This query above:
Calculates the value, placing the value into the rate field.
Converts the value into megabytes, placing the result into the mbrate field.
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:
newstring := concat(["Hello","world"])Instead, you must assign the values to a field in the event stream and concatenate that:
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:
groupBy(username)Which is equivalent to:
groupBy(field=username)The type is also still implied in this situation, so to group by a list of fields:
groupBy([username,groupname])When used with other parameters, the implied parameter still does not need to be specified:
groupBy(status_code, function=count)Parameter names do not need to be defined in any sequence; there is no order needed. The function call:
groupBy(field=status_code, function=count)Is the same as:
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.