Rule definitions

Rules are used to infer new data at query time for read queries.

They are defined in a schema and represent a logic embedded in a data model.

Behavior

Rules behavior

A rule definition is bounded to rule’s label and includes a condition and a conclusion. Defining the same rule label will overwrite the rule.

Whenever a read transaction with the inference option enabled has a query executed, it checks whether any rule in a schema is applicable. Reasoning engine checks every rule’s conclusion first. If a conclusion can add some results to the query’s output, then the rule’s condition is checked. If the condition is met, then query results are added as if the rule’s conclusion was inserted to the database prior to the query.

No inferred data is persisted in a database, but there is an inference cache existing within a transaction.

The results inferred by rules are virtual because they exist only within a transaction. They are not persisted in a database, and any data inferred in a query ceases to exist when the transaction is closed.

Syntax

A Define query consists of a single define clause and always starts with the define keyword.

Define queries are written in TypeQL with the following syntax:

define <pattern>

A pattern in a define clause uses TypeQL to declare type hierarchy, ownerships, roleplayers, and rules. For type hierarchy, ownerships, and roleplayers, see the Type definitions page. For rules, see below.

Rule syntax

A rule consists of a when clause with a condition and a then clause with a conclusion.

Rules are defined in a schema with the following syntax:

define

rule <label>: when {
    <condition>
} then {
    <conclusion>
};

When clause

A when clause contains a condition of a rule. A condition is a pattern to seek for and to apply a conclusion to. It works much like a match clause pattern in an Insert query.

A rule’s condition can be a pattern with multiple statements, including disjunctions and negations. When using a disjunction in a rule, the disjunctive parts must be bound by variables outside the or statement. These variables are the only ones permitted in the then clause.

Then clause

A then clause contains a conclusion of a rule. A conclusion describes a single relation or ownership of an attribute (due to Horn-clause logic) that is considered virtually existent in a database if the rule’s condition is met.

There are exactly three distinct conclusions permitted:

  1. A new relation.

  2. Ownership of an attribute defined by its value.

  3. Ownership of an attribute defined by a variable.

The conclusion must be insertable, according to a schema, (e.g., we can’t give ownership of an attribute to an instance that can’t own that attribute type).

A rule’s conclusion can’t use variables that aren’t defined in the rule’s condition. A rule must not insert any instance that occurs negated in its condition (a pattern in its when clause) or in a condition of any rule it may trigger. Attempting to define such a rule will throw an error to prevent an infinite rule.

Rules will not create duplicates of instances that are already in a database or have already been inferred. Hence, there is no need to check if an instance already exists in a database before inserting it.

Since version 2.18.0, we can use typeql::schema/type-definitions.adoc#_abstract types in a rule as long as all the type variables that define which instances to create during materialization are concrete (non-abstract).

Examples

Basic rule

A simple schema with a rule to add an attribute ownership to all instances oif a type should look like the following:

define

full-name sub attribute, value string;

subject sub entity;
user sub subject;
person sub user, owns full-name;

rule universal-alias: when {
    $p isa person;
} then {
    $p has full-name "Dude";
};

In the above example all person entities matched by a read query with the inference option enabled will have a full-name attribute with the value Dude.

Using arithmetics

We can use arithmetics and other built-in functions in rules. Be careful not to create an infinite progression with it.

define

size-kb sub attribute, value long;
size-mb sub attribute, value double;

object sub entity;
resource sub object;
file sub resource, owns size-kb, owns size-mb;

rule size-covert: when {
    $f isa file, has size-kb $s;
    ?mb = $s/1024;
} then {
    $f has size-mb ?mb;
};

The above query defines an additional attribute subtype size-mb, defines that it can be owned by the file entity subtype, and creates a rule size-convert to create ownership of size-mb with the value 1024 times lower than size-kb to any file instance that has size-kb. The rule uses a value variable for arithmetics.

The value of size-mb is not persisted in a database. Instead, it is inferred by the size-covert rule every time we do a read transaction with the inference option enabled and use size-mb in a query.

Transitive rule

define

rule transitive-reachability: when {
    (from: $x, to: $y) isa rel;
    (from: $y, to: $z) isa rel;
} then {
    (from: $x, to: $z) isa rel;
};

The above example allows for the transitivity of relations of the rel type for roles from and to. We can interpret this rule as joining two relations together. It creates a relation from x to z, given that there are relations of from x to y and from y to z.

Advanced transitivity usage

When inferring relations, it is possible to variablize any part of both condition and conclusion. For example, if you want a rule to infer transitivity for any type of relations, use a rule such as:

define

rule all-relation-types-are-transitive: when {
    ($role1: $x, $role2: $y) isa! $relation;
    ($role1: $y, $role2: $z) isa! $relation;
} then {
    ($role1: $x, $role2: $z) isa $relation;
};

The above rule adds transitivity for any rule with at least two roles. Note that all roles, roleplayers, and relation types are variablized, making this rule non-selective and universal.

Complex rule

Let’s see an example of a more complex rule from the IAM schema. For the full IAM schema, see the iam-schema.tql file in our documentation repository.

Complex rule example
define

rule add-view-permission: when {
    $modify isa action, has name "modify_file";
    $view isa action, has name "view_file";
    $ac_modify (object: $obj, action: $modify) isa access;
    $ac_view (object: $obj, action: $view) isa access;
    (subject: $subj, access: $ac_modify) isa permission;
} then {
    (subject: $subj, access: $ac_view) isa permission;
};

The above rule make that the permission to access any file with the action that has name of view_file can be inferred by the rule from the permission to use modify_file action on the same file.

Provide Feedback