-
Notifications
You must be signed in to change notification settings - Fork 72
Remove complicated examples from patterns #976
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
krishnangovindraj
merged 3 commits into
typedb:3.x-development
from
krishnangovindraj:remove-complicated-examples-from-patterns
Aug 20, 2025
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
198 changes: 198 additions & 0 deletions
198
core-concepts/modules/ROOT/pages/typeql/invalid-patterns.adoc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
= Advanced: Invalid Patterns | ||
|
||
This section dives into the semantics of TypeQL, | ||
using the <<_disjunctive_normal_form>> to explain why <<_disjoint_variable_reuse>> makes a pattern invalid. | ||
|
||
|
||
== Disjoint variable reuse | ||
This section demonstrates some of the cases which can arise due to | ||
xref:new_core_concepts::typeql/query-variables-patterns.adoc#_disjoint_variable_usage_errors[disjoint variable reuse] | ||
|
||
[unbound negations] | ||
=== Unbound negation inputs | ||
Notice we can write a pattern where the input variable to a negation is not bound in the parent. | ||
For example: | ||
[,typeql] | ||
---- | ||
match | ||
$p1 isa person; $p2 isa person; | ||
{ | ||
$emp1 isa employment, links (employer: $company, employee: $p1); | ||
} or { | ||
$edu1 isa education, links (institute: $institute, attendee: $p2); | ||
}; | ||
|
||
not { $emp2 isa employment, links (employer: $company, employee: $p2); }; | ||
not { $edu2 isa employment, links (institute: $institute, attendee: $p2); }; | ||
---- | ||
At first glance, this looks like a reasonable query: | ||
we query for persons `$p1` and `$p2` who neither worked for the same company, | ||
nor attended the same institute. | ||
However, you can see that the input variables for the negations (`$company` and `$institute`) | ||
are not local to the negation, but also not bound in the parent conjunction. | ||
|
||
==== Disjunctive Normal Form | ||
The best way to think about these requirements is to convert the query to Disjunctive Normal Form | ||
by rewriting the pattern using "distributivity" and examining each branch: | ||
|
||
`A; { B; } or { C; };` becomes `{A; B} or {A; C};` | ||
|
||
In this case, we get the pattern: | ||
[,typeql] | ||
---- | ||
match | ||
{ | ||
$p1 isa person; $p2 isa person; | ||
$emp1 isa employment, links (employer: $company, employee: $p1); | ||
not { $emp2 isa employment, links (employer: $company, employee: $p2); }; | ||
not { $edu2 isa employment, links (institute: $institute, attendee: $p2); }; | ||
} or { | ||
$p1 isa person; $p2 isa person; | ||
$edu1 isa education, links (institute: $institute, attendee: $p2); | ||
not { $emp2 isa employment, links (employer: $company, employee: $p2); }; | ||
not { $edu2 isa education, links (institute: $institute, attendee: $p2); }; | ||
}; | ||
---- | ||
Although this could now be a valid logic query, | ||
the first branch requires that `$p2` did not attend *any* institute, | ||
and the second branch requires that `$p2` was not employed by *any* employer. | ||
This is clearly not what we intended to write. Hence, we flag these as invalid TypeQL queries. | ||
|
||
[NOTE] | ||
==== | ||
There is, of course, a way to express the intended query: | ||
[,typeql] | ||
.The correct query | ||
---- | ||
$p1 isa person; $p2 isa person; | ||
not { | ||
$emp1 isa employment, links (employer: $company, employee: $p1); | ||
$emp2 isa employment, links (employer: $company, employee: $p2); | ||
}; | ||
not { | ||
$edu1 isa education, links (institute: $institute, attendee: $p2); | ||
$edu2 isa education, links (institute: $institute, attendee: $p2); | ||
}; | ||
---- | ||
==== | ||
|
||
=== Reusing branch local variables in disjunctions | ||
|
||
Consider another case of questionable query composition: | ||
[,typeql] | ||
---- | ||
match | ||
$p1 isa person; $p2 isa person; | ||
{ | ||
$emp1 isa employment, links (employer: $company, employee: $p1); | ||
} or { | ||
$edu1 isa education, links (institute: $institute, attendee: $p2); | ||
}; | ||
{ | ||
$emp2 isa employment, links (employer: $company, employee: $p2); | ||
} or { | ||
$edu2 isa education, links (institute: $institute, attendee: $p2); | ||
}; | ||
---- | ||
Ideally, this would be a query to find two persons `$p1` and `$p2` who | ||
were either employed by the same company, or attended the same institute. | ||
|
||
The DNF quickly reveals the mistake: | ||
[,typeql] | ||
---- | ||
match | ||
{ | ||
$p1 isa person; $p2 isa person; | ||
$emp1 isa employment, links (employer: $company, employee: $p1); | ||
$emp2 isa employment, links (employer: $company, employee: $p2); | ||
} or { | ||
$p1 isa person; $p2 isa person; | ||
$edu1 isa education, links (institute: $institute, attendee: $p2); | ||
$emp2 isa employment, links (employer: $company, employee: $p2); | ||
} or | ||
{ | ||
$p1 isa person; $p2 isa person; | ||
$emp1 isa employment, links (employer: $company, employee: $p1); | ||
$edu2 isa education, links (institute: $institute, attendee: $p2); | ||
} or { | ||
$p1 isa person; $p2 isa person; | ||
$edu1 isa education, links (institute: $institute, attendee: $p2); | ||
$edu2 isa education, links (institute: $institute, attendee: $p2); | ||
}; | ||
---- | ||
|
||
You can see the query we meant to write in two of those branches: | ||
[,typeql] | ||
---- | ||
match | ||
$p1 isa person; $p2 isa person; | ||
{ | ||
$emp1 isa employment, links (employer: $company, employee: $p1); | ||
$emp2 isa employment, links (employer: $company, employee: $p2); | ||
} or { | ||
$edu1 isa education, links (institute: $institute, attendee: $p2); | ||
$edu2 isa education, links (institute: $institute, attendee: $p2); | ||
}; | ||
---- | ||
|
||
The problem lies in the other two branches. | ||
[,typeql] | ||
---- | ||
match | ||
{ | ||
$p1 isa person; $p2 isa person; | ||
$emp1 isa employment, links (employer: $company, employee: $p1); | ||
$edu2 isa education, links (institute: $institute, attendee: $p2); | ||
} or { | ||
$p1 isa person; $p2 isa person; | ||
$edu1 isa education, links (institute: $institute, attendee: $p2); | ||
$emp2 isa employment, links (employer: $company, employee: $p2); | ||
}; | ||
---- | ||
This will return any persons `$p1` & `$p2` when | ||
either (1) `$p1` is employed by *any* and `$p2` attended *any* institute; | ||
or (2) `$p2` is employed by *any* company and `$p1` attended *any* institute. | ||
|
||
Notice `$company` is "internal" to both the first and second disjunctions (The same is the case for `$institute`). | ||
TypeQL throws a "disjoint variable re-use" error for such cases. | ||
|
||
=== Single origin for optionals | ||
Optional variables can only be used in only one `try` block in a given match clause. Consider: | ||
[,typeql] | ||
---- | ||
match | ||
try { $x isa person, has name "John"; }; | ||
try { $x isa person, has name "James"; }; | ||
---- | ||
The reason this is banned is similar to the <<_unbound_negation_inputs>> case above: | ||
It is unclear if `$x` is an input or either of the `try` blocks, or an optional variable. | ||
|
||
The same variable can be used in `try` blocks in the next stage. | ||
This is because the variable is bound - either to a concept, or to `None` - and is an 'input' to the block. | ||
The block simply fails if it tries to use a variable bound to `None` in a constraint. | ||
|
||
[NOTE] | ||
==== | ||
The semantics of try blocks dictate the equivalence of | ||
|
||
`try { P };` and `{ P } or { not { P' }; };` | ||
|
||
where, `P'` is the pattern obtained by renaming all the 'optional' variables in `P` with fresh ones. | ||
Rewriting the above query with this equivalence (assuming `$x` to be 'optional' and renaming it, although this is ambiguous) | ||
[,tyepql] | ||
---- | ||
{ $x "John"; } or not { $x_1 "John"; }; | ||
{ $x "James"; } or not { $x_2 "James"; }; | ||
---- | ||
|
||
Taking the DNF gives us 4 cases, and some very unintuitive behaviour: | ||
[,typeql] | ||
---- | ||
{ $x "John"; $x "James"; } or # persons named both James and John. | ||
{ $x "John"; not { $x_2 "James"; }; } or # If nobody is named James, then persons named John | ||
{ not { $x_1 "John"; }; $x "James"; } or # If nobody is named John, then persons named James | ||
{ not { $x_1 "John"; }; not { $x_2 "James"; }; }; # If nobody is named either, then a single answer `None` | ||
# If there is one person named John, and a different person named James, then no answers | ||
---- | ||
|
||
==== |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're happy with the state of it, we can include it here (and maybe in the nav)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you can include it. Any page we have i think that is 'live' should be in the nav!
You can also link to it from the query-variables-patterns page i think!