rule
Definition
In different cases we want to rewrite the VQL Veezoo generates from a user's question. We might want to add filters, change which function Veezoo uses in specific situations, correct potential errors, or insert our own definitions of concepts. With Measures
and default filters
Veezoo provides frameworks to rewrite VQL in a simple and straightforward fashion. rules
are a more general method. They detect patterns in the generated VQL and modify them in a desired fashion.
For almost all cases there is a way to achieve the desired result using other tools than rules, in which case we strongly recommend to use the alternative instead.
Structure
Rules follow this structure:
rule only_show_active_customers {
rule_pattern: """
// Matching Pattern
var customer: kb.Customer
// Modification Pattern
@add_if_not_exists {
customer.is_active = true
}
"""
// specify which user role should have this logic applied
for_user_role: "Marketing"
// specify the kind of rule
rule_tag: meta.SemanticLogicTag
comment: "This is a description of the rule and its motivations."
}
rule_pattern
The rule pattern consists of two parts: the Matching Pattern and the Modification Pattern.
The Matching Pattern can be a variety of VQL statements and serves two functions:
- Variable declaration:
var customer: kb.Customers
declares the variablecustomer
, which then can be referred to later on. - Rule condition: Veezoo checks the Matching Pattern to see whether a rule should be applied. This rule comes into play only if everything listed in the Matching Pattern is also found in the VQL of the Question.
Additionally we can use these functions to decide if the rule should be applied or not:
- @missing { ... }: the rule should not be used, if the pattern in the brackets is present
- { ... } @or { ... }: the rule should be applied when either pattern is present
The Modification Pattern defines the actual behavior of the rule. It takes its arguments from within its brackets. The following are the available Modification Patterns:
- @add_if_not_exists { ... }: adds a pattern to the VQL
- @remove { ... }: removes a pattern from the VQL, if present
- @remove_connected { ... }: similar to @remove, but also removes all classes connected to the pattern as well
- @replace { ... } with { ... }: replaces a specific pattern with another
@add_if_not_exists { ... }
This Modification Pattern is mostly used to add additional constraints or filters, the user didn't explicitly ask for, but we, as the data team, still want to be there. Let's look at the example given above again:
rule only_show_active_customers {
rule_pattern: """
var customer: kb.Customer
@add_if_not_exists {
customer.is_active = true
}
"""
for_user_role: "Marketing"
}
In this example, we want to ensure that users with the user_role
Marketing can only see active customers.
- In the first line we define the variable
customer
and simultaneously state that this rule should be triggered if the patternkb.Customer
is present in the VQL. - We then use the Modification Pattern: if the filter
customer.is_active = true
is not present, we add it, such that all customers shown in the result are active. - We add the
for_user_role
such that this rule only applies to the marketing users (as explained later)
A similar effect to this can be achieved by using default filters. There are two differences: default filters can be removed by business users, where as this rule will always be applied, and default filters can't be set for specific user roles.
@remove { ... }
With this Modification Pattern we can remove filters that Veezoo might add, but we don't want in certain cases.
Consider the following scenario: we have two datasets, Employees containing information about employees, including their status (active or inactive), and Transactions containing records of sales transactions, each associated with an employee. We want to calculate the total sales amount without filtering out inactive employees, as we need to consider sales made by all employees, regardless of their current status.
To this end, we use the following rule:
rule total_sales_all_employees {
rule_pattern:
"""
var transactions: kb.Transactions
var employee: kb.Employee
sum(transactions.amount)
transactions.with_Employee = employee
@remove {
# Remove the filter that only includes active employees
employee.is_Active = true
}
"""
}
In the Matching Pattern we first define the variables transactions
and employee
and ensure that the rule is only applied when calculating the sum total of transactions. We specify that transactions is linked with employee using transactions.with_Employee = employee
.
Using the @remove pattern, we then explicitly remove the filter that restricts the data to only active employees (employee.is_Active = true
).
By applying this rule, we ensure that the total sales are calculated without excluding any employees based on their status. This way, sales made by inactive employees are also considered in the total, providing a complete picture of sales performance.
@replace { ... } with { ... }
This Modification Patterns is used to replace lines in VQL. A typical example of this is, when Veezoo would normally use a certain function, but we want to use a different one in a specific case. In the following example, we will show two uses of this rule pattern working together.
Consider the following situation. We have an Orders
dataset where each order has an Order_Qty
column. The Order_Qty
values are adjusted based on business logic, such as 1 for regular orders, 0 for orders that shouldn't be counted, and -1 for orders that should decrease the count. This should be used when counting the orders instead of a COUNT DISTINCT
on an order id.
This rule does this: it first checks if the question contains kb.Orders
, and if so replaces all instances of the function count(orders)
with sum(orders.Order_Qty)
.
rule Substitute_Count_For_Sum {
rule_pattern:
"""
var orders: kb.Orders
@replace {
count(orders)
} with {
sum(orders.Order_Qty)
}
"""
}
In a next step, we would like to adapt the average function. Let's say we would like to figure out the average value of an order last month. The default average function might just calculate the average Value
of all Orders
. This, again, would be false. We therefore replace the default average(orders.Value)
with sum(orders.Value) / count(orders)
:
rule Adapt_Average {
rule_pattern:
"""
var orders: kb.Orders
@replace {
average(orders.Value)
} with {
sum(orders.Value) / count(orders)
}
"""
}
Note that this rule uses count(orders)
. So, when asking Average value of an order last month? Veezoo would first replace average(orders.Value)
with sum(orders.Value) / count(orders)
, and then in a second step this with sum(orders.Value) / sum(orders.Order_Qty)
. If we would only ever be interested in the average, we could of course only have one rule do both.
for_user_role (optional)
If there are user roles defined, you can use this tag to specify to whom the rule should apply. This can be useful, for example, if different roles have differing definitions of KPIs. For a more detailed explanation, see here.
rule_tag (optional)
This tag specifies when the rule is applied. There are two options:
- use
meta.SemanticLogicTag
if the rule should be applied in the Semantic Rewriting stage - use
meta.LogicRewritingTag
if the rule should be applied in the Logic Rewriting stage
Semantic Rewriting stage
This Rewriting stage is concerned with rewriting rules that need to be communicated to the user for clarity when answering.
Classical examples are default values, e.g. "how many customers" means "how many active customers" by default and the user should be made aware of that, in case that's not what he/she wants.
Logic Rewriting stage
In contrast to Semantic Rewriting, rules in Logic Rewriting are rather technical and have no meaning for the end user.
A common case is when the database does not have the sought concept raw stored, but it should be computed from other concepts. We call these higher-order concepts.
These are usually KPI definitions (measure
), e.g. to compute the profit we need to add revenue x with revenue y and subtract costs z, but not costs w.
The main difference for users will be if the rule affects the title of answers or not. If no rule_tag
is specified, meta.SemanticLogicTag
will be used by default.
comment (optional)
You can add descriptions to your rules to explain what it does. Comments have to use the comment
tag to be saved. // comments using slashes
will not cause any errors when compiling, but will be lost upon reloading the page.