Put stage
Syntax
As a put stage may insert data, the body of a put stage is a sequence of valid insert statements.
put <insert-statement> [ <insert-statement> ... ]
Example
Schema for the following examples
#!test[schema, commit]
define
entity content owns id;
entity page sub content,
owns page-id,
owns name;
entity profile sub page,
owns username;
entity user sub profile,
owns phone,
owns karma;
attribute id value string;
attribute page-id sub id;
attribute username sub page-id;
attribute name value string;
attribute phone value string;
attribute karma value double;
attribute start-date, value date;
relation friendship relates friend @card(0..2), owns start-date;
user plays friendship:friend;
attribute age, value integer;
user owns age;
The following query creates a friendship relation between two users, if one does not already exist.
#!test[write]
match
$user-1 isa user, has username "Alice";
$user-2 isa user, has username "Bob";
put
friendship (friend: $user-1, friend: $user-2);
Behavior
The behavior of a put
stage can be described in pseudo-code as:
def run_put(pattern, input_rows):
output_rows = []
for row in input_rows:
matched_rows = run_match(pattern, row)
if matched_rows.is_empty():
inserted_row = run_insert(pattern, row)
output_rows += [inserted_row]
else:
output_rows += matched_rows
return output_rows
Hence, a put stage is expected to be only as performant as running both the match and the insert stage.
Multiple inputs will cause the pattern to be inserted multiple times. For example, assume there are two
What happens in this case is:
This behavior is currently under review. Please open a Github issue if it is blocking your progress! |
No partial matching
The put matches the entire pattern or inserts the entire pattern. Partial matching and partial inserts are not performed.
To illustrate, consider running the following queries sequentially.
#!test[write]
# Creates some initial data
put $p isa user, has name "Alice";
# Does nothing, as the match succeeds
put $p1 isa user, has name "Alice";
# Creates a new user with name "Bob"
put $p2 isa user, has name "Bob";
# Creates a new user with name "Alice" & age 10.
put $p3 isa user, has name "Alice", has age 10;
The statement put $p3…
creates a new user because the whole pattern fails to match,
as there is no existing user with name "Alice" AND age 10.
To achieve the desired result of not re-creating the user if only the age attribute is missing,
put
stages can be pipelined.
#!test[write]
put $p4 isa user, has name "Bob"; # Matches the user with name "Bob", since it exists
put $p4 has age 11; # Adds an attribute to the same user
Isolation from concurrent transactions
Though put stages are meant to create data patterns which don’t exist in the database, they still run under the snapshot-isolation of TypeDB transactions. This means they may insert data even if a concurrent transaction has inserted data which satisfies the match - leading to duplication if both transactions commit.
As cardinality restrictions (@card
, @key
, and @unique
) are validated across concurrent transactions,
ensuring that a put
pattern contains atleast one constraint with such a restriction will ensure that the data is inserted at most once across all transactions.