Disjunctions

Disjunctive patterns can be used in match stages. They allow queries to match several cases (or “branches”) of patterns in parallel.

Syntax

{ <pattern1> } or { <pattern2> } [ or { <pattern3> } ... ] ;

The individual blocks { <pattern-i> } of a disjunction are called its branches.

It is worth highlighting that, like all patterns, disjunctions can be chained, e.g.:

{ <p1> } or { <p2> };
{ <p3> } or { <p4> };

Moreover, since disjunctions are themselves patterns, they can also be nested, e.g.:

{ <p1> }
or {
  { <p2> } or { <p3> };
};

Behavior

A disjunction pattern is satisfied if one of its branches is satisfied.

Schema for the following examples
#!test[schema, commit]
define
    attribute email, value string;
    attribute phone, value string;
    attribute username, value string;
    relation friendship, relates friend @card(0..2);
    relation marriage, relates spouse @card(0..2);
    relation policy, relates covered @card(0..);
    entity user,
        owns username, owns email, owns phone,
        plays friendship:friend;
    entity person, plays marriage:spouse, plays policy:covered;

Variable scopes

A variable that appears in every branch will appear in the answers to the disjunction, and is considered to be in the scope of the parent pattern. Results to the following query will contain answers for $x and $id:

#!test[read]
match
  $x isa user;
  { $x has email $id; } or { $x has phone $id; };

A variable that occurs in only one branch AND not outside the disjunction is considered local to the branch. Results to the following query will only contain answers for $x:

#!test[read]
match
  $x isa user;
  { $x has email $e; } or { $x has phone $p; };

In line with the unique scope principle, a variable that is local to more than one pattern is invalid. The following query is invalid as $e has multiple scopes.

#!test[read, fail_at=runtime]
match
  $x isa user;
  { $x has email $e; } or { $x has phone $p; };
  not { $e contains "@typedb.com"; };

Branches are non-exclusive and unordered

A disjunction is a true logical “or”, not a conditional: every branch whose pattern is satisfied contributes answers, and the branches are evaluated in no particular order. A branch is never “skipped” because an earlier branch already matched.

This matters whenever only some answers of the resulting stream are kept — for example with return first in a scalar function or a limit stage in a pipeline. Consider this attempt at a tiered classification, reading or as if it were an if/else if/else:

#!test[schema]
define
fun classify($score: integer) -> string:
match
  { $score > 8; let $label = "high"; }
  or { $score > 4; let $label = "medium"; }
  or { let $label = "low"; };
return first $label;

The function defines and type-checks, but does not do what the if/else reading suggests. The last branch is unguarded, so it is satisfied for every $score, and for a score of 10 all three branches are satisfied. The function’s match therefore produces an answer per satisfied branch, and since branches are unordered, return first may return any of "high", "medium", or "low" — for any input.

Re-declaring the function with a stream return makes the behavior visible: every satisfied branch yields an answer.

#!test[schema]
define
fun classify_all($score: integer) -> { string }:
match
  { $score > 8; let $label = "high"; }
  or { $score > 4; let $label = "medium"; }
  or { let $label = "low"; };
return { $label };

#!test[read, count=3]
match
  let $label in classify_all(10);

When a single answer is to be selected from a stream produced by a disjunction — with return first, return last, or a limit stage — write the branches to be mutually exclusive, so that at most one branch can be satisfied by any answer:

#!test[schema]
define
fun classify_tiered($score: integer) -> string:
match
  { $score > 8; let $label = "high"; }
  or { $score > 4; $score <= 8; let $label = "medium"; }
  or { $score <= 4; let $label = "low"; };
return first $label;

Examples

  1. Retrieve friends and friends of friends of a user;

    #!test[read]
    match
      $x isa user, has username "john_1";
      $y isa user;
      { friendship($x, $y); }
      or { friendship($x, $z); friendship($z, $y); };