Constraint

If we query a heavily used system to show us a list of all user inputs it has ever received, the result will be more data than we can handle. We need to filter it down, perhaps to a specific time frame or event, for example. These filters we add are called constraints. Constraints are used to specify which session, transaction, and event properties are relevant for our query. Not every query needs a constraint; they are only necessary if we wish to add a layer of specificity. If we're exploring our data, we may first want to see an overview of what's there, and then refine the query with constraints to zoom in on what we need. Constraints are separated from the selection by a colon symbol (:) and can consist of multiple terms, all separated by commas.

In our example query the constraint part is t.e.userInput != "" :

la s.id, t.e.userInput, t.e2.answerText:  t.e.userInput != "" limit 30

The command and selection in this example are used to tell TQL to show us all the user input + bot response pairs, along with the session ID they belong to. The added constraint excludes the input + response pairs that are blank, i.e. those where the person chatting with the bot hit enter without typing a message. The constraint syntax allows us to specify conditions that must be met in order to include those particular properties in the selection. We can use standard logical operators as well as regular expressions for constraints.

Core constraint operators

TQL supports the eight basic constraint operators listed in the table below. The table shows a brief explanation of their function, an indication of whether or not they support regular expressions, what field types they apply to, and example event properties they can be applied to.

Operator Function Support regex Applies to Example event properties
== equals no strings, numeric, date userInput, index, beginTime
!= not equals no strings, numeric, date userInput, index, beginTime
~= matches yes strings userInput, folder, fname
!~ not matches yes strings userInput, folder, fname
~~ contains no analyzed text userInputWords, answerTextWords
!~~ not contains no analyzed text userInputWords, answerTextWords
exists exists no any any
notexists does not exist no any any
>, <, >=, <= greater than, less than, greater or equal than, less or equal than no numeric, date transactionsCount, beginTime,

The exists and notexists operators are special in that they only take one argument (the event property to check) and return a match if the constraint is met, e.g. if the parameter exists when we are using the exists operator.

The 'contains' operator ~~ can be used to perform case-insensitive queries of userInputWords and answerTextWords, by checking if a field contains or does not contain a word or word sequence. This is possible because these two fields are stored as analyzed text, which is lowercased, tokenized, and normalized before being stored as an array of tokens (words).

Advanced constraints

TQL offers two advanced possibilities you can use to construct especially precise queries: sub queries and skip constraints.

Sub queries

Sub queries allow you to nest a complete TQL query in a main query and access the results in the selection. This is useful, for example, in exploring aggregate results such as averages, etc. The schematic for sub queries is:

x = @( TQL query )

This example shows our sample query adapted as a sub query. In the selection we display unique occurrences of the ID we found:

lu result.id: result = @(la s.id as id, t.e.userInput, t.e2.answerText:  t.e.userInput != "")

We explore sub queries in more detail here.

Skip constraints

Skip constraints helps the you find inputs that come after a specific input that triggered a flow. For example when you want to see what input came right after the user hits the Safetynet fallback flow. The skip-to syntax specifies the order of precedence among transitions or events by setting out a start point and an end point, where the start or end point has some property. The syntax (in pseudo-code) is schematically as follows:

start -{end point constraints}> end point
end point <{start point constraints}- start

The start and end points can be transactions or events (not necessarily in the same transaction). The constraints enclosed by {} specify the constraints that the start or end point must fulfill. The direction of the arrow specifies whether the constraint is skipping forward or backward in the session.

We'll give you a closer view of the syntax as well as some use cases on our How to page about using skip constraints.

Was this page helpful?