Officially out now: The TypeDB 3.0 Roadmap >>

Inserting data

This page explains how to insert data in a TypeDB database.

Understanding insert pipeline stages

An insert stage is used to insert data into a database. It is usually executed in write transactions.

It can be used as a single query, meaning the insertion will occur exactly once. Alternatively, it can be a stage in a pipeline, potentially with multiple input rows from the preceding stage (e.g., a match stage).

Refer to the Insert stage page for a detailed explanation and additional examples.

Sending insert pipelines

The examples below can be combined with the following schema file. A part of this file is defined in the respective definition guides.

See the schema.tql
schema.tql
define
  entity content @abstract,
    owns id @key;

  entity page @abstract, sub content,
    owns page-id,
    owns name,
    owns bio,
    owns profile-picture,
    plays posting:page,
    plays following:page;

  entity profile @abstract, sub page,
    owns username,
    owns name @card(0..3),
    plays group-membership:member,
    plays location:located,
    plays content-engagement:author,
    plays following:follower;

  entity user sub profile,
    owns email,
    owns phone @regex("^\d{8,15}$") @unique,
    owns karma,
    owns relationship-status,
    plays friendship:friend,
    plays family:relative,
    plays relationship:partner,
    plays marriage:spouse,
    plays employment:employee;

  relation social-relation @abstract,
    relates related @card(0..);

  relation friendship sub social-relation,
    relates friend as related @card(0..);

  relation family sub social-relation,
    relates relative as related @card(0..1000);

  relation relationship sub social-relation,
    relates partner as related,
    owns start-date;

  relation marriage sub relationship,
    relates spouse as partner,
    owns exact-date,
    plays location:located;

  entity organisation sub profile,
    owns tag @card(0..100),
    plays employment:employer;

  entity company sub organisation;
  entity charity sub organisation;

  relation employment,
    relates employer,
    relates employee,
    owns start-date,
    owns end-date;

  entity group sub page,
    owns group-id,
    owns tag @card(0..100),
    plays group-membership:group;

  relation group-membership,
    relates group,
    relates member,
    owns start-timestamp,
    owns end-timestamp;

  entity post @abstract, sub content,
    owns post-id,
    owns post-text,
    owns creation-timestamp @range(1970-01-01T00:00:00..),
    owns tag @card(0..10),
    plays posting:post,
    plays commenting:parent,
    plays reaction:parent,
    plays location:located;

  entity text-post sub post;

  entity image-post sub post,
    owns post-image;

  entity comment sub content,
    owns comment-id,
    owns comment-text,
    owns creation-timestamp,
    owns tag @card(0..5),
    plays commenting:comment,
    plays commenting:parent,
    plays reaction:parent;

  relation interaction @abstract,
    relates subject @abstract,
    relates content;

  relation content-engagement @abstract, sub interaction,
    relates author as subject;

  relation posting sub content-engagement,
    relates page as content,
    relates post @card(0..1000);

  relation commenting sub content-engagement,
    relates parent as content,
    relates comment;

  relation reaction sub content-engagement,
    relates parent as content,
    owns emoji @values("like", "love", "funny", "surprise", "sad", "angry"),
    owns creation-timestamp;

  relation following,
    relates follower,
    relates page;

  entity place,
    owns place-id,
    owns name,
    plays location:place;

  entity country sub place,
    plays city-location:parent;

  entity city sub place,
    plays city-location:city;

  relation location,
    relates place,
    relates located;

  relation city-location sub location,
    relates parent as place,
    relates city as located;

  attribute id @abstract, value string;
  attribute page-id @abstract, sub id;
  attribute username sub page-id;
  attribute group-id sub page-id;
  attribute post-id sub id;
  attribute comment-id sub id;
  attribute place-id sub id;

  attribute name value string;
  attribute email value string @regex("^.*@\w+\.\w+$");
  attribute phone value string;
  attribute karma value double;
  attribute relationship-status value string @values("single", "married", "other");
  attribute latitude value double;
  attribute longitude value double;

  attribute event-date @abstract, value datetime;
  attribute start-date sub event-date;
  attribute end-date sub event-date;
  attribute exact-date @abstract, sub event-date;

  attribute payload @abstract, value string;
  attribute text-payload @abstract, sub payload;
  attribute image-payload @abstract, sub payload;
  attribute bio sub text-payload;
  attribute comment-text sub text-payload;
  attribute post-text sub text-payload;
  attribute post-image sub image-payload;
  attribute profile-picture sub image-payload;

  attribute tag value string;
  attribute emoji value string;

  attribute creation-timestamp value datetime;
  attribute start-timestamp value datetime;
  attribute end-timestamp value datetime;

  fun all_relatives($user: user) -> { user }:
    match
      $relative isa user;
      {
        family (relative: $user, relative: $relative);
      } or {
        let $intermediate in all_relatives($user);
        family (relative: $intermediate, relative: $relative);
      };
      return { $relative };

Insert an instance

To insert data, open a write transaction. This allows you to read and write data to your database while keeping the schema secure.

Every insertion begins with an insert. Similar to type definition, multiple statements can be combined to insert multiple instances and establish connections between them.

To insert a type instance (an entity, a relation, or an attribute), use the isa keyword:

User insertion example
insert $u isa user;

All data inserted into a database is verified against the schema constraints (e.g., existence of supertypes, traits, annotations, etc.).

Some verifications occur during query execution ("operation time"), while others wait until commit for your convenience. For example, the above query works with a minimal schema like this:

Minimal schema for the user insertion example
define entity user;

However, if the full example schema is used, a key attribute is required for every inserted user, so it is impossible to use this insert query only.

Insert an instance with ownership

To insert both an entity and an owned attribute, combine isa and has statements in a single expression:

User with a key insertion example
insert
  $u isa user, has username "User";
Why is username a key?

If you explore the schema, you’ll see that user s inherit an abstract key attribute id from content. While abstract attributes cannot have instances, this definition states that any id owned by a content is a key attribute. At the same time, username is an attribute with a supertype id, meaning it is an id. It is also inherited by user s from profile. Thus, a username can serve as a user 's key: a unique and singular identifier!

Insert a relation

Similarly, multiple users can be inserted and connected through a relation. To insert a relation, use the links keyword, specifying roleplayers with the (role: $var) format. The expression can also be simplified to an anonymous relation with a short declaration (see the full syntax in the next example):

Friendship with two friends insertion example
insert
  $a isa user, has username "Alice", has email "alice@typedb.com";
  $b isa user, has username "Bob", has email "bob@typedb.com";
  (friend: $a, friend: $b) isa friendship;

Inserting data based on a match

Using pipelines, you can match and modify your data in multiple stages, creating complex queries and automations. A simple example is an insert stage based on results of a match stage.

Suppose we send the following query with an insert stage following a match stage:

Match-insert example
match
  $u isa user, has username "Bob";
insert
  $new-u isa user, has username "Charlie", has email "charlie@typedb.com";
  $f isa friendship, links (friend: $u, friend: $new-u);

With the data inserted earlier on this page, we should match only one user with the username "Bob". This results in inserting one new user and one relevant relation. If multiple matches are made by the match clause, the insert clause executes for each match (in our case, this would lead to an error due to a duplicate key "Charlie," but it might be useful in other scenarios). If no matches are made, nothing is inserted.

Refer to Data pipelines for more information.

Response interpretation

An insert stage returns all concepts inserted into the database as a stream of concept rows. A collected stream of concept rows can be interpreted as a table with a header containing variables and rows with respective concept answers for each variable. This lets you verify if the result meets your expectations.

For example, the following is a snapshot of a TypeDB Console’s response to the previous query (iid s may vary):

Match-insert example’s result
   ------------
    $f     | iid 0x1f00020000000000000001 isa friendship
    $new-u | iid 0x1e00030000000000000003 isa user
    $u     | iid 0x1e00030000000000000000 isa user
   ------------

Preserving or reverting changes

Do not forget to commit your transaction to preserve the changes. As mentioned earlier, some data constraints are only validated upon commit. This means you might discover inconsistencies in your data during the commit process, and the commit will be rejected.

While most validations occur during operation time to keep data synchronized, commit-time validations are necessary to allow flexible data modifications. For example, validating inserted data against cardinality during operation time could block modifications entirely.

If you want to abort your changes, simply close the transaction or perform a rollback. Refer to Transactions for more details.

Having troubles?

Refer to the Debugging queries page for common debugging tips.