TypeDB 3.0 is live! Get started for free.

Java driver tutorial

In this tutorial, we’ll build a sample application with the Java driver capable of basic interaction with TypeDB:

  • Connect to a TypeDB server,

  • Manage databases and transactions,

  • Send different types of queries.

Follow the steps below or see the full source code.

See the full source code
import com.typedb.driver.TypeDB;
import com.typedb.driver.api.Credentials;
import com.typedb.driver.api.Driver;
import com.typedb.driver.api.DriverOptions;
import com.typedb.driver.api.Transaction;
import com.typedb.driver.api.answer.ConceptRow;
import com.typedb.driver.api.answer.JSON;
import com.typedb.driver.common.exception.TypeDBDriverException;
import com.typedb.driver.jni.TypeDBDriver;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class Main {
    private static final String DB_NAME = "sample_app_db";
    private static final String SERVER_ADDR = "127.0.0.1:1729";

    public enum Edition {
        CORE,
        CLOUD
    }

    private static final Edition TYPEDB_EDITION = Edition.CORE;
    private static final String USERNAME = "admin";
    private static final String PASSWORD = "password";

    public static void main(String[] args) {
        try (Driver driver = driverConnect(TYPEDB_EDITION, SERVER_ADDR, USERNAME, PASSWORD)) {
            if (dbSetup(driver, DB_NAME, false)) {
                System.out.println("Setup complete.");
                queries(driver, DB_NAME);
            } else {
                System.out.println("Setup failed.");
            }
        } catch (TypeDBDriverException e) {
            e.printStackTrace();
        }
    }

    private static void queries(Driver driver, String dbName) throws TypeDBDriverException {
        System.out.println("Request 1 of 6: Fetch all users as JSON objects with emails and phone numbers");
        List<JSON> users = fetchAllUsers(driver, dbName);

        String new_user_phone = "17778889999";
        String new_user_email = "k.koolidge@typedb.com";
        String new_user_username = "k-koolidge";
        System.out.printf("Request 2 of 6: Add a new user with the email '%s' and phone '%s'\n", new_user_email, new_user_phone);
        List<ConceptRow> newUsers = insertNewUser(driver, dbName, new_user_email, new_user_phone, new_user_username);

        String kevinEmail = "kevin.morrison@typedb.com";
        System.out.printf("Request 3 of 6: Find direct relatives of a user with email %s\n", kevinEmail);
        List<ConceptRow> directRelatives = getDirectRelativesByEmail(driver, dbName, kevinEmail);

        System.out.printf("Request 4 of 6: Transitively find all relatives of a user with email %s\n", kevinEmail);
        List<ConceptRow> allRelatives = getAllRelativesByEmail(driver, dbName, kevinEmail);

        String oldKevinPhone = "110000000";
        String newKevinPhone = "110000002";
        System.out.printf("Request 5 of 6: Update the phone of a of user with email %s from %s to %s\n", kevinEmail, oldKevinPhone, newKevinPhone);
        List<ConceptRow> updatedUsers = updatePhoneByEmail(driver, dbName, kevinEmail, oldKevinPhone, newKevinPhone);

        System.out.printf("Request 6 of 6: Delete the user with email \"%s\"%n", new_user_email);
        deleteUserByEmail(driver, dbName, new_user_email);
    }

    private static Driver driverConnect(Edition edition, String uri, String username, String password) throws TypeDBDriverException {
        if (edition == Edition.CORE) {
            Driver driver = TypeDB.coreDriver(
                    uri,
                    new Credentials(username, password),
                    new DriverOptions(false, null)
            );
            return driver;
        }
        if (edition == Edition.CLOUD) {
            Driver driver = TypeDB.cloudDriver(
                    Set.of(uri),
                    new Credentials(username, password),
                    new DriverOptions(true, null)
            );
            return driver;
        }
        return null;
    }

    private static List<JSON> fetchAllUsers(Driver driver, String dbName) throws TypeDBDriverException {
        try (Transaction tx = driver.transaction(dbName, Transaction.Type.READ)) {
            String query = "match $u isa user; fetch { 'phone': $u.phone, 'email': $u.email };";
            List<JSON> answers = tx.query(query).resolve().asConceptDocuments().stream().collect(Collectors.toList());
            answers.forEach(json -> System.out.println("JSON: " + json.toString()));
            return answers;
        }
    }

    public static List<ConceptRow> insertNewUser(Driver driver, String dbName, String newEmail, String newPhone, String newUsername) throws TypeDBDriverException {
        try (Transaction tx = driver.transaction(dbName, Transaction.Type.WRITE)) {
            String query = String.format(
                    "insert $u isa user, has $e, has $p, has $username; $e isa email '%s'; $p isa phone '%s'; $username isa username '%s';",
                    newEmail, newPhone, newUsername
            );
            List<ConceptRow> answers = tx.query(query).resolve().asConceptRows().stream().collect(Collectors.toList());
            tx.commit();
            for (ConceptRow row : answers) {
                String phone = row.get("p").get().tryGetString().get();
                String email = row.get("e").get().tryGetString().get();
                System.out.println("Added new user. Phone: " + phone + ", E-mail: " + email);
            }
            return answers;
        }
    }

    public static List<ConceptRow> getDirectRelativesByEmail(Driver driver, String dbName, String email) throws TypeDBDriverException {
        try (Transaction tx = driver.transaction(dbName, Transaction.Type.READ)) {
            List<ConceptRow> users = tx.query(String.format("match $u isa user, has email '%s';", email)).resolve().asConceptRows()
                    .stream().collect(Collectors.toList());
            if (users.size() != 1) {
                System.out.printf("Error: Found %d users with email %s, expected 1", users.size(), email);
                return null;
            } else {
                String relativesQuery = String.format(
                        "match " +
                                "$e == '%s';" +
                                "$u isa user, has email $e;" +
                                "$family isa family ($u, $relative);" +
                                "$relative has username $username;" +
                                "not { $u is $relative; };" +
                                "select $username;" +
                                "sort $username asc;",
                        email
                );
                List<ConceptRow> rows = tx.query(relativesQuery).resolve().asConceptRows().stream().collect(Collectors.toList());
                rows.forEach(row -> System.out.println("Relative: " + row.get("username").get().tryGetString().get()));
                return rows;
            }
        }
    }

    public static List<ConceptRow> getAllRelativesByEmail(Driver driver, String dbName, String email) throws TypeDBDriverException {
        try (Transaction tx = driver.transaction(dbName, Transaction.Type.READ)) {
            List<ConceptRow> users = tx.query(String.format("match $u isa user, has email '%s';", email)).resolve().asConceptRows()
                    .stream().collect(Collectors.toList());
            if (users.size() != 1) {
                System.out.printf("Error: Found %d users with email %s, expected 1", users.size(), email);
                return null;
            } else {
                String relativesQuery = String.format(
                        "match " +
                                "$u isa user, has email $e;" +
                                "$e == '%s';" +
                                "let $relative in all_relatives($u);" +
                                "not { $u is $relative; };" +
                                "$relative has username $username;" +
                                "select $username;" +
                                "sort $username asc;",
                        email
                );
                List<ConceptRow> rows = tx.query(relativesQuery).resolve().asConceptRows().stream().collect(Collectors.toList());
                rows.forEach(row -> System.out.println("Relative: " + row.get("username").get().tryGetString().get()));
                return rows;
            }
        }
    }

    public static List<ConceptRow> updatePhoneByEmail(Driver driver, String dbName, String email, String oldPhone, String newPhone) throws TypeDBDriverException {
        List<ConceptRow> rows = new ArrayList<>();
        try (Transaction tx = driver.transaction(dbName, Transaction.Type.WRITE)) {
            String query = String.format(
                    "match $u isa user, has email '%s', has phone $phone; $phone == '%s';" +
                            "update $u has phone '%s';",
                    email, oldPhone, newPhone);
            rows = tx.query(query).resolve().asConceptRows().stream().collect(Collectors.toList());
            tx.commit();
            System.out.printf("Total number of phones updated: %d%n", rows.size());
        }
        return rows;
    }

    public static void deleteUserByEmail(Driver driver, String dbName, String email) throws TypeDBDriverException {
        try (Transaction tx = driver.transaction(dbName, Transaction.Type.WRITE)) {
            String query = String.format("match $u isa user, has email '%s'; delete $u;", email);
            List<ConceptRow> rows = tx.query(query).resolve().asConceptRows().stream().collect(Collectors.toList());
            tx.commit();
            System.out.printf("Deleted %d users", rows.size());
        }
    }

    private static boolean dbSetup(Driver driver, String dbName, boolean dbReset) throws TypeDBDriverException {
        System.out.println("Setting up the database: " + dbName);
        if (driver.databases().contains(dbName)) {
            if (dbReset) {
                if (!replaceDatabase(driver, dbName)) {
                    return false;
                }
            } else {
                System.out.println("Found a pre-existing database. Do you want to replace it? (Y/N) ");
                String answer;
                try {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                    answer = reader.readLine();
                } catch (IOException e) {
                    throw new RuntimeException("Failed to read user input.", e);
                }
                if (answer.equalsIgnoreCase("y")) {
                    if (!replaceDatabase(driver, dbName)) {
                        return false;
                    }
                } else {
                    System.out.println("Reusing an existing database.");
                }
            }
        } else { // No such database found on the server
            if (!createDatabase(driver, dbName)) {
                System.out.println("Failed to create a new database. Terminating...");
                return false;
            }
        }
        if (driver.databases().contains(dbName)) {
            return validateData(driver, dbName);
        } else {
            System.out.println("Database not found. Terminating...");
            return false;
        }
    }

    private static boolean createDatabase(Driver driver, String dbName) throws TypeDBDriverException {
        System.out.print("Creating a new database...");
        driver.databases().create(dbName);
        System.out.println("OK");
        dbSchemaSetup(driver, dbName);
        dbDatasetSetup(driver, dbName);
        return true;
    }

    private static boolean replaceDatabase(Driver driver, String dbName) throws TypeDBDriverException {
        System.out.print("Deleting an existing database...");
        driver.databases().get(dbName).delete();  // Delete the database if it exists already
        System.out.println("OK");
        if (createDatabase(driver, dbName)) {
            return true;
        } else {
            System.out.println("Failed to create a new database. Terminating...");
            return false;
        }
    }

    private static void dbSchemaSetup(Driver driver, String dbName) throws TypeDBDriverException {
        String schemaFile = "schema.tql";
        try (Transaction tx = driver.transaction(dbName, Transaction.Type.SCHEMA)) {
            String defineQuery = new String(Files.readAllBytes(Paths.get(schemaFile)));
            System.out.print("Defining schema...");
            tx.query(defineQuery).resolve();
            tx.commit();
            System.out.println("OK");
        } catch (IOException e) {
            throw new RuntimeException("Failed to read the schema file.", e);
        }
    }

    private static void dbDatasetSetup(Driver driver, String dbName) throws TypeDBDriverException {
        String dataFile = "data_small_single_query.tql";
        try (Transaction tx = driver.transaction(dbName, Transaction.Type.WRITE)) {
            String insertQuery = new String(Files.readAllBytes(Paths.get(dataFile)));
            System.out.print("Loading data...");
            tx.query(insertQuery).resolve();
            tx.commit();
            System.out.println("OK");
        } catch (IOException e) {
            throw new RuntimeException("Failed to read the data file.", e);
        }
    }

    private static boolean validateData(Driver driver, String dbName) throws TypeDBDriverException {
        try (Transaction transaction = driver.transaction(dbName, Transaction.Type.READ)) {
            String countQuery = "match $u isa user; reduce $count = count;";
            System.out.print("Validating the dataset...");
            long count = transaction.query(countQuery).resolve().asConceptRows().next().get("count").get().tryGetInteger().get();
            if (count == 3) {
                System.out.println("Passed");
                return true;
            } else {
                System.out.printf("Validation failed, unexpected number of users: %d. Terminating...\n", count);
                return false;
            }
        }
    }
}

Environment setup

To run this sample application, you’ll need:

  1. TypeDB: either a TypeDB Cloud cluster or a self-hosted deployment. For installation instructions, see the Installation manual page.

  2. Java and TypeDB Java driver. For the driver installation instructions, see the Java driver page.

Imported modules

To be able to use the TypeDB Java driver API in the Sample application, use the following import statements:

import com.typedb.driver.TypeDB;
import com.typedb.driver.api.Credentials;
import com.typedb.driver.api.Driver;
import com.typedb.driver.api.DriverOptions;
import com.typedb.driver.api.Transaction;
import com.typedb.driver.api.answer.ConceptRow;
import com.typedb.driver.api.answer.JSON;
import com.typedb.driver.common.exception.TypeDBDriverException;
import com.typedb.driver.jni.TypeDBDriver;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

Default values

We store default values as constants in the source code:

private static final String DB_NAME = "sample_app_db";
private static final String SERVER_ADDR = "127.0.0.1:1729";

public enum Edition {
    CORE,
    CLOUD
}

private static final Edition TYPEDB_EDITION = Edition.CORE;
private static final String USERNAME = "admin";
private static final String PASSWORD = "password";

where DB_NAME — the name of the database to use; SERVER_ADDR — address of the TypeDB server to connect to; TYPEDB_EDITION — TypeDB Community Edition or Cloud edition selector; USERNAME/PASSWORD — authentication credentials.

Program structure

The main workflow of this sample application includes establishing a connection to TypeDB, database setup, and querying.

public static void main(String[] args) {
    try (Driver driver = driverConnect(TYPEDB_EDITION, SERVER_ADDR, USERNAME, PASSWORD)) {
        if (dbSetup(driver, DB_NAME, false)) {
            System.out.println("Setup complete.");
            queries(driver, DB_NAME);
        } else {
            System.out.println("Setup failed.");
        }
    } catch (TypeDBDriverException e) {
        e.printStackTrace();
    }
}

The entire main() function code is executed in the context of the network connection, represented by the driver object that is returned by the function.

TypeDB connection

The driverConnect() function takes edition and addr as mandatory parameters.

private static Driver driverConnect(Edition edition, String uri, String username, String password) throws TypeDBDriverException {
    if (edition == Edition.CORE) {
        Driver driver = TypeDB.coreDriver(
                uri,
                new Credentials(username, password),
                new DriverOptions(false, null)
        );
        return driver;
    }
    if (edition == Edition.CLOUD) {
        Driver driver = TypeDB.cloudDriver(
                Set.of(uri),
                new Credentials(username, password),
                new DriverOptions(true, null)
        );
        return driver;
    }
    return null;
}

The edition is expected to be an Enum for selecting a TypeDB edition. Depending on the TypeDB edition selected, this function initializes either a TypeDB Community Edition or a TypeDB Cloud / Enterprise connection.

TypeDB connections require objects of the Credentials (authentication credentials) and DriverOptions (driver-specific connection options like TLS settings) classes. For our sample application, we have suitable default values set for all editions.

Database setup

To set up a TypeDB database, we need to make sure that it exists and has the correct schema and data. First, we check whether a database with the provided name already exists on the server.

If such a database doesn’t exist, we create a new database, define its schema, and load initial data.

To prevent data loss, avoid deleting an existing database without confirmation from a user.

If a database with the specified name already exists, we check whether we need to replace it. To do so, we check the dbReset parameter, and, if it’s false, ask for an input from a user. If any of the two suggesting replacement of the database is acceptable, we replace the database by deleting the existing database and then creating a new one.

As the final step of the database setup, we test it.

private static boolean dbSetup(Driver driver, String dbName, boolean dbReset) throws TypeDBDriverException {
    System.out.println("Setting up the database: " + dbName);
    if (driver.databases().contains(dbName)) {
        if (dbReset) {
            if (!replaceDatabase(driver, dbName)) {
                return false;
            }
        } else {
            System.out.println("Found a pre-existing database. Do you want to replace it? (Y/N) ");
            String answer;
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                answer = reader.readLine();
            } catch (IOException e) {
                throw new RuntimeException("Failed to read user input.", e);
            }
            if (answer.equalsIgnoreCase("y")) {
                if (!replaceDatabase(driver, dbName)) {
                    return false;
                }
            } else {
                System.out.println("Reusing an existing database.");
            }
        }
    } else { // No such database found on the server
        if (!createDatabase(driver, dbName)) {
            System.out.println("Failed to create a new database. Terminating...");
            return false;
        }
    }
    if (driver.databases().contains(dbName)) {
        return validateData(driver, dbName);
    } else {
        System.out.println("Database not found. Terminating...");
        return false;
    }
}

Creating a new database

We create a new database with the specified name (sample_app_db by default) and call functions to define its schema and load initial data.

private static boolean createDatabase(Driver driver, String dbName) throws TypeDBDriverException {
    System.out.print("Creating a new database...");
    driver.databases().create(dbName);
    System.out.println("OK");
    dbSchemaSetup(driver, dbName);
    dbDatasetSetup(driver, dbName);
    return true;
}

Replacing a database

We delete a database with the specified name (sample_app_db by default) and call a function to create a new one instead:

private static boolean replaceDatabase(Driver driver, String dbName) throws TypeDBDriverException {
    System.out.print("Deleting an existing database...");
    driver.databases().get(dbName).delete();  // Delete the database if it exists already
    System.out.println("OK");
    if (createDatabase(driver, dbName)) {
        return true;
    } else {
        System.out.println("Failed to create a new database. Terminating...");
        return false;
    }
}

Defining a schema

We use a define query to define a schema for the newly created database:

private static void dbSchemaSetup(Driver driver, String dbName) throws TypeDBDriverException {
    String schemaFile = "schema.tql";
    try (Transaction tx = driver.transaction(dbName, Transaction.Type.SCHEMA)) {
        String defineQuery = new String(Files.readAllBytes(Paths.get(schemaFile)));
        System.out.print("Defining schema...");
        tx.query(defineQuery).resolve();
        tx.commit();
        System.out.println("OK");
    } catch (IOException e) {
        throw new RuntimeException("Failed to read the schema file.", e);
    }
}

The schema for the sample application is stored in the schema.tql file.

See the full schema
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 };

We use a database name passed as a parameter to open a transaction. Then we send the contents of the file as a TypeQL define query and commit the changes made by the transaction.

Loading initial data

With the schema defined, we can load initial data into our database with the insert query:

private static void dbDatasetSetup(Driver driver, String dbName) throws TypeDBDriverException {
    String dataFile = "data_small_single_query.tql";
    try (Transaction tx = driver.transaction(dbName, Transaction.Type.WRITE)) {
        String insertQuery = new String(Files.readAllBytes(Paths.get(dataFile)));
        System.out.print("Loading data...");
        tx.query(insertQuery).resolve();
        tx.commit();
        System.out.println("OK");
    } catch (IOException e) {
        throw new RuntimeException("Failed to read the data file.", e);
    }
}

We read the data_small_single_query.tql file, send its contents as a single query, and then commit the changes.

See the full insert query
data_small_single_query.tql
insert
    $u1 isa user,
        has username "masako-holley",
        has phone "185800100011",
        has email "masako.holley@typedb.com";

    $u2 isa user,
        has username "pearle-goodman",
        has phone "171255522222",
        has email "pearle.goodman@typedb.com";

    $u3 isa user,
        has username "kevin-morrison",
        has phone "110000000",
        has email "kevin.morrison@typedb.com";

    $relatives1 isa family (relative: $u1, relative: $u2);
    $relatives2 isa family (relative: $u2, relative: $u3);

Testing a database

With the schema defined and data loaded, we test our database to make sure it’s ready. To test the database, we send a query to count the number of users in the database:

private static boolean validateData(Driver driver, String dbName) throws TypeDBDriverException {
    try (Transaction transaction = driver.transaction(dbName, Transaction.Type.READ)) {
        String countQuery = "match $u isa user; reduce $count = count;";
        System.out.print("Validating the dataset...");
        long count = transaction.query(countQuery).resolve().asConceptRows().next().get("count").get().tryGetInteger().get();
        if (count == 3) {
            System.out.println("Passed");
            return true;
        } else {
            System.out.printf("Validation failed, unexpected number of users: %d. Terminating...\n", count);
            return false;
        }
    }
}

Query examples

After database setup is complete, we proceed with querying our database with different types of queries in the queries() function:

private static void queries(Driver driver, String dbName) throws TypeDBDriverException {
    System.out.println("Request 1 of 6: Fetch all users as JSON objects with emails and phone numbers");
    List<JSON> users = fetchAllUsers(driver, dbName);

    String new_user_phone = "17778889999";
    String new_user_email = "k.koolidge@typedb.com";
    String new_user_username = "k-koolidge";
    System.out.printf("Request 2 of 6: Add a new user with the email '%s' and phone '%s'\n", new_user_email, new_user_phone);
    List<ConceptRow> newUsers = insertNewUser(driver, dbName, new_user_email, new_user_phone, new_user_username);

    String kevinEmail = "kevin.morrison@typedb.com";
    System.out.printf("Request 3 of 6: Find direct relatives of a user with email %s\n", kevinEmail);
    List<ConceptRow> directRelatives = getDirectRelativesByEmail(driver, dbName, kevinEmail);

    System.out.printf("Request 4 of 6: Transitively find all relatives of a user with email %s\n", kevinEmail);
    List<ConceptRow> allRelatives = getAllRelativesByEmail(driver, dbName, kevinEmail);

    String oldKevinPhone = "110000000";
    String newKevinPhone = "110000002";
    System.out.printf("Request 5 of 6: Update the phone of a of user with email %s from %s to %s\n", kevinEmail, oldKevinPhone, newKevinPhone);
    List<ConceptRow> updatedUsers = updatePhoneByEmail(driver, dbName, kevinEmail, oldKevinPhone, newKevinPhone);

    System.out.printf("Request 6 of 6: Delete the user with email \"%s\"%n", new_user_email);
    deleteUserByEmail(driver, dbName, new_user_email);
}

The queries are as follows:

  1. Fetch query — to retrieve information in a JSON format

  2. Insert query — to insert new data into the database

  3. Match query — to retrieve data from the database as rows

  4. Match query with a function call — to retrieve data from the database as rows using functions

  5. Update query — to replace data in the database

  6. Delete query — to delete data from the database

Every query is implemented as a function that includes some output of the query response and returns some meaningful data.

Fetch query

Fetching allows you to retrieve data from a TypeDB database as JSON documents.

Let’s use a fetch pipeline to fetch phone s and email s for all user s in the database:

private static List<JSON> fetchAllUsers(Driver driver, String dbName) throws TypeDBDriverException {
    try (Transaction tx = driver.transaction(dbName, Transaction.Type.READ)) {
        String query = "match $u isa user; fetch { 'phone': $u.phone, 'email': $u.email };";
        List<JSON> answers = tx.query(query).resolve().asConceptDocuments().stream().collect(Collectors.toList());
        answers.forEach(json -> System.out.println("JSON: " + json.toString()));
        return answers;
    }
}

We get the response as a stream of results, containing JSONs. We create an answers variable to store the list of JSONs and iterate through it to print the JSONs.

Insert query

Let’s insert a new user with username, phone, and email attributes to the database.

public static List<ConceptRow> insertNewUser(Driver driver, String dbName, String newEmail, String newPhone, String newUsername) throws TypeDBDriverException {
    try (Transaction tx = driver.transaction(dbName, Transaction.Type.WRITE)) {
        String query = String.format(
                "insert $u isa user, has $e, has $p, has $username; $e isa email '%s'; $p isa phone '%s'; $username isa username '%s';",
                newEmail, newPhone, newUsername
        );
        List<ConceptRow> answers = tx.query(query).resolve().asConceptRows().stream().collect(Collectors.toList());
        tx.commit();
        for (ConceptRow row : answers) {
            String phone = row.get("p").get().tryGetString().get();
            String email = row.get("e").get().tryGetString().get();
            System.out.println("Added new user. Phone: " + phone + ", E-mail: " + email);
        }
        return answers;
    }
}

The insert query returns an Iterator of ConceptRow s: one for every insert clause execution. We collect the Iterator to a list to store the inserted data. Then we commit the changes, print phone s and email s by iterating though the list of ConceptRow s, and return the stored list.

Since the insert query has no match clause, the insert clause is executed exactly once. Insert queries always return an ConceptRowIterator, where every ConceptRow represents an inserted result: column names (variable names) and their respective concepts.

Match query

If we don’t need to convert the result into a JSON document, we can use a single match stage, which returns ConceptRow s similarly to insert.

Let’s retrieve all direct relatives for a user using its email.

public static List<ConceptRow> getDirectRelativesByEmail(Driver driver, String dbName, String email) throws TypeDBDriverException {
    try (Transaction tx = driver.transaction(dbName, Transaction.Type.READ)) {
        List<ConceptRow> users = tx.query(String.format("match $u isa user, has email '%s';", email)).resolve().asConceptRows()
                .stream().collect(Collectors.toList());
        if (users.size() != 1) {
            System.out.printf("Error: Found %d users with email %s, expected 1", users.size(), email);
            return null;
        } else {
            String relativesQuery = String.format(
                    "match " +
                            "$e == '%s';" +
                            "$u isa user, has email $e;" +
                            "$family isa family ($u, $relative);" +
                            "$relative has username $username;" +
                            "not { $u is $relative; };" +
                            "select $username;" +
                            "sort $username asc;",
                    email
            );
            List<ConceptRow> rows = tx.query(relativesQuery).resolve().asConceptRows().stream().collect(Collectors.toList());
            rows.forEach(row -> System.out.println("Relative: " + row.get("username").get().tryGetString().get()));
            return rows;
        }
    }
}

The getDirectRelativesByEmail() method checks that there is only one user matched with the email provided by an input parameter. It then executes the query to find the relatives, collects the results, and iterates through them to print the username of every matched relative.

For bigger numbers of results, it might be faster to iterate through a stream, rather than collect and store the results first.

Match query with a function call

Let’s change the query a little to get not only direct relatives, but all relatives of a user. Additionally, let’s use a TypeDB function called all_relatives that we previously defined in the schema.

public static List<ConceptRow> getAllRelativesByEmail(Driver driver, String dbName, String email) throws TypeDBDriverException {
    try (Transaction tx = driver.transaction(dbName, Transaction.Type.READ)) {
        List<ConceptRow> users = tx.query(String.format("match $u isa user, has email '%s';", email)).resolve().asConceptRows()
                .stream().collect(Collectors.toList());
        if (users.size() != 1) {
            System.out.printf("Error: Found %d users with email %s, expected 1", users.size(), email);
            return null;
        } else {
            String relativesQuery = String.format(
                    "match " +
                            "$u isa user, has email $e;" +
                            "$e == '%s';" +
                            "let $relative in all_relatives($u);" +
                            "not { $u is $relative; };" +
                            "$relative has username $username;" +
                            "select $username;" +
                            "sort $username asc;",
                    email
            );
            List<ConceptRow> rows = tx.query(relativesQuery).resolve().asConceptRows().stream().collect(Collectors.toList());
            rows.forEach(row -> System.out.println("Relative: " + row.get("username").get().tryGetString().get()));
            return rows;
        }
    }
}

Update query

Let’s replace a phone of one of the user s by a new one. The fastest way is to use an update stage, which replaces the old data with the specified values:

public static List<ConceptRow> updatePhoneByEmail(Driver driver, String dbName, String email, String oldPhone, String newPhone) throws TypeDBDriverException {
    List<ConceptRow> rows = new ArrayList<>();
    try (Transaction tx = driver.transaction(dbName, Transaction.Type.WRITE)) {
        String query = String.format(
                "match $u isa user, has email '%s', has phone $phone; $phone == '%s';" +
                        "update $u has phone '%s';",
                email, oldPhone, newPhone);
        rows = tx.query(query).resolve().asConceptRows().stream().collect(Collectors.toList());
        tx.commit();
        System.out.printf("Total number of phones updated: %d%n", rows.size());
    }
    return rows;
}

We could also do that by deleting ownership of the old path attribute from the file entity and assigning it with ownership of the new path attribute manually by writing consecutive delete and insert pipeline stages.

Executing multiple write stages in a single transaction isolates these changes from other transactions: there won’t be any point of time for other TypeDB users where they won’t see a user s phone. Moreover, if any other transaction makes a conflicting change before we commit this transaction, then our transaction fails upon a commit.

Delete query

Finally, let’s delete a user by a given email. It can be simply done by a single statement inside a match, and a very short delete operation.

public static void deleteUserByEmail(Driver driver, String dbName, String email) throws TypeDBDriverException {
    try (Transaction tx = driver.transaction(dbName, Transaction.Type.WRITE)) {
        String query = String.format("match $u isa user, has email '%s'; delete $u;", email);
        List<ConceptRow> rows = tx.query(query).resolve().asConceptRows().stream().collect(Collectors.toList());
        tx.commit();
        System.out.printf("Deleted %d users", rows.size());
    }
}

Learn more

The full source code of this sample application.

The full API reference for the TypeDB Java driver.