A polymorphic database powered by types
TypeDB is a polymorphic database with a conceptual data model, a strong subtyping system, a symbolic reasoning engine, and a beautiful and elegant type-theoretic language: TypeQL.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
match $user isa user,
has full-name $name,
has email $email;
# This returns all users of any type
match $user isa employee,
has full-name $name,
has email $email,
has employee-id $id;
# This returns only users who are employees
match $user-type sub user;
$user isa $user-type,
has full-name $name,
has email $email;
# This returns all users and their type
Harness subtyping to trivially write polymorphic queries, returning data of multiple types by querying a common supertype. Queries are automatically resolved against the schema to retrieve all data matching the declared pattern. Variablize any part of your queries to return types and roles along with data. As new types are added to the schema, they are automatically included in the results of pre-existing queries against their supertype, so queries never have to be refactored.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
insert
$john isa full-time-employee,
has full-name "John Doe",
has primary-email "[email protected]",
has email "[email protected]",
has email "[email protected]",
has employee-id 183;
$readme isa file, has path "/usr/johndoe/repos/typedb/readme.md";
$edit isa action, has name "edit file";
$kevin isa user, has email "[email protected]";
$perm (subject: $john, object: $readme, action: $edit) isa permission;
$rqst (target: $perm, requestee: $kevin) isa change-request, has requested-change "revoke";
Model your data naturally in terms of entities, the relations between them, and the attributes they have. There's no need for separate conceptual and logical models due to database limitations. Entities, relations and attributes are all first-class citizens, allowing you to easily implement multi-valued attributes, n-ary relations, and nested relations. With the freedom to define role names, you are no longer bound by restrictive subject-predicate-object frameworks. In TypeDB, the conceptual data model is the logical data model.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
define
full-name sub attribute, value string;
id sub attribute, value string;
email sub id;
employee-id sub id;
user sub entity,
owns full-name,
owns email @unique;
employee sub user,
owns employee-id @key,
plays team-membership:member;
membership sub relation, abstract,
relates parent,
relates member;
team-membership sub membership,
relates team as parent;
Entities, relations, and attributes are defined in the schema using the type system, allowing a type's owned attributes and roles played in relations to be intuitively inherited by its subtypes. Inheritance ensures conceptual consistency between the type hierarchy and declared type behaviours, and allows the schema to be trivially extended with no migrations or downtime. Create abstract types and override supertype properties within the schema, bringing the expressive power of OOP to the database layer.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
define
rule transitive-team-membership: when {
(team: $team-1, member: $team-2) isa team-membership;
(team: $team-2, member: $member) isa team-membership;
} then { (team: $team-1, member: $member) isa team-membership; };
insert
$john isa user, has email "[email protected]";
$eng isa team, has name "Engineering ";
$cloud isa team, has name "Cloud";
(team: $eng, member: $cloud) isa team-membership;
(team: $cloud, member: $john) isa team-membership;
match
$john isa user, has email "[email protected]";
(team: $team, member: $john) isa team-membership;
# This will return both Cloud and Engineering for $team due to the defined rule
Define rules in your schema using first-order logic, enabling new facts to be derived from existing data and business logic to be encoded in the data model. Reasoning can be triggered sequentially and recursively producing complex permutative behaviour from simple rules. Reasoned facts are generated at query time using the most recent data, and so are never stale while minimizing disk usage. They can always be interrogated to discover their root-causes using TypeDB’s Explanations feature.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
match
$user isa user;
match
$user isa user, has email "[email protected]";
match
$user isa user, has email "[email protected]";
(subject: $user, object: $tql, action: $action) isa permission;
$tql isa file, has extension "tql";
match
$user isa user;
(subject: $user, object: $tql, action: $action) isa permission;
$tql isa file, has extension "tql";
(team: $eng, member: $user) isa team-membership;
$eng isa team, has name "Engineering";
TypeQL works through declarative pattern matching, allowing queries to be scoped to any level in the type hierarchy without considering physical data stores or execution strategy. As a composable language, queries can be easily modified by simply adding to or removing from the query pattern. Concatenating two valid patterns will always produce a new valid one. Thanks to the high level of abstraction and near-natural syntax, domain experts can instantly understand a query’s intent even with no knowledge of TypeQL.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
define
weekly-hours sub attribute value long;
full-time-employee sub employee;
part-time-employee sub employee, owns weekly-hours;
insert
$francois isa full-time-employee,
has full-name "François Durand",
has email "[email protected]",
has employee-id 184,
has weekly-hours 35;
# [THW03] Invalid Write: Attribute of type 'weekly-hours' is
# not defined to be owned by type 'full-time-employee'.
With declarative schemas backed by static type checking, all queries are validated to ensure nonsensical writes are automatically blocked, and nonsensical reads throw an exception instead of returning an empty result set. This applies to both schema and data queries guaranteeing integrity and semantic correctness for all interactions with the database. Sessions and transactions are also strongly typed, preventing unauthorized and unintentional modifications from committing.
Solve object-relational mismatch entirely within the database
Object-relational mismatch has plagued engineers since OOP became dominant. Relational databases were never built to contend with object models and cannot model inheritance. Both document and graph databases struggle to represent data outside their niches, while multi-model databases do little to solve the fundamental mismatch problem. In all contemporary databases, the focus is on storage format rather than the abstract model itself. TypeDB is unique in implementing conceptual data models without forcing them into predefined structures.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
insert
$john isa full-time-employee,
has full-name "John Doe",
has primary-email "[email protected]",
has email "[email protected]",
has email "[email protected]",
has employee-id 183;
$readme isa file, has path "/usr/johndoe/repos/typedb/readme.md";
$edit isa action, has name "edit file";
$kevin isa user, has email "[email protected]";
$perm (subject: $john, object: $readme, action: $edit) isa permission;
$rqst (target: $perm, requestee: $kevin) isa change-request, has requested-change "revoke";
TypeDB uses the Enhanced Entity-Relationship model with a declarative schema and static type checking. This allows the natural implementation of a type hierarchy, multivalued attributes, and n-ary and nested relations. Leverage OOP concepts like abstraction, inheritance, and polymorphism without warping the conceptual model. Normalization, null values, and reification are things of the past.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
DO $$
DECLARE
inserted_user_id INTEGER;
inserted_resource_id INTEGER;
inserted_action_id INTEGER;
inserted_requestee_id INTEGER;
inserted_permission_id INTEGER;
BEGIN
INSERT INTO users (id, full_name)
VALUES (DEFAULT, "John Doe")
RETURNING id INTO inserted_user_id;
INSERT INTO user_emails (user_id, email, is_primary)
VALUES
(inserted_user_id, "[email protected]", TRUE),
(inserted_user_id, "[email protected]", FALSE),
(inserted_user_id, "[email protected]", FALSE);
INSERT INTO employees (user_id, employee_id)
VALUES (inserted_user_id, 183);
INSERT INTO full_time_employees (user_id)
VALUES (inserted_user_id);
INSERT INTO resources (id)
VALUES (DEFAULT)
RETURNING id INTO inserted_resource_id;
INSERT INTO files (inserted_resource_id, path)
VALUES (inserted_resource_id, "/usr/johndoe/repos/typedb/readme.md");
INSERT INTO actions (id, name)
VALUES (DEFAULT, "edit file")
RETURNING id INTO inserted_action_id;
INSERT INTO users (id, full_name)
VALUES (DEFAULT, NULL)
RETURNING id INTO inserted_requestee_id;
INSERT INTO user_emails (user_id, email, is_primary)
VALUES (inserted_requestee_id, "[email protected]", FALSE);
INSERT INTO permissions (id, subject, object, action)
VALUES (DEFAULT, inserted_user_id, inserted_resource_id, inserted_action_id)
RETURNING id INTO inserted_permission_id;
INSERT INTO change_requests (id, target, requestee, requested_change)
VALUES (DEFAULT, inserted_permission_id, inserted_requestee_id, "revoke");
COMMIT;
END $$;
SQL was designed in a pre-OOP era. Schemas and queries quickly grow out of control when implementing class-table inheritance and normalizing multivalued attributes. Inheritance is also impossible to implement without losing referential integrity. This can only be fixed with brittle constraint-defining syntax that requires continual maintenance or integrity control in the application layer.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
db.change_requests.aggregate([
{
$lookup: {
from: "permissions",
localField: "target",
foreignField: "_id",
as: "permission"
}
},
{
$unwind: {
path: "$permission"
}
},
{
$lookup: {
from: "users",
localField: "user",
foreignField: "_id",
as: "user"
}
},
{
$unwind: {
path: "$user"
}
},
{
$lookup: {
from: "users",
localField: "permission.user",
foreignField: "_id",
as: "permission.user"
}
},
{
$unwind: {
path: "$permission.user"
}
},
{
$lookup: {
from: "identifiers",
localField: "permission.object",
foreignField: "_id",
as: "permission.object"
}
},
{
$unwind: {
path: "$permission.object"
}
},
{
$lookup: {
from: "actions",
localField: "permission.action",
foreignField: "_id",
as: "permission.action"
}
},
{
$unwind: {
path: "$permission.action"
}
},
{
$project: {
_id: false,
user: "$permission.user.email",
requestee: "$user.email",
target: "$permission.object.identifier",
action: "$permission.action.name",
request: "$req_change"
}
}
])
Document databases are designed and optimized to store hierarchical data. They can easily store multivalued attributes as list fields and null values as the absence of fields. However, inserting and retrieving highly interconnected data is complicated and poorly optimized. All similarly shaped data is dumped into the same collection without a schema, leaving the backend developer responsible for maintaining integrity and structure.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
MATCH
(john:User),
(readme:File {path: "/vaticle/repos/typedb/readme.md"}),
(edit:Action {name: "edit file"})
WHERE (
john.primary_email = "[email protected]"
OR "[email protected]" IN john.alias_emails
) AND NOT EXISTS {
MATCH (john)<-[:SUBJECT]-(perm:Permission)-[:OBJECT]->(readme)
WHERE EXISTS ( (perm)-[:ACTION]->(edit) )
}
WITH john, readme, edit
CREATE (john)<-[:SUBJECT]-(perm:Permission)-[:OBJECT]->(readme)
WITH edit, perm
CREATE (perm)-[:ACTION]->(edit);
MATCH
(perm:Permission),
(perm)-[:SUBJECT]->(john:User),
(perm)-[:OBJECT]->(readme:File {id: "/vaticle/repos/typedb/readme.md"}),
(perm)-[:ACTION]->(edit:Action {name: "edit file"}),
(kevin:User)
WHERE (
john.primary_email = "[email protected]"
OR "[email protected]" IN john.alias_emails
) AND (
kevin.primary_email = "[email protected]"
OR "[email protected]" IN kevin.alias_emails
)
CREATE
(rqst:ChangeRequest {requested_change: "revoke"}),
(rqst)-[:TARGET]->(perm),
(rsqt)-[:REQUESTEE]->(kevin);
Graph databases, despite excelling at storing traditional graph-like data, cannot natively express relations other than strictly binary ones between two entities. This forces you to reify the model: turn relationships from edges into nodes, blurring the lines between entity and relation. Polymorphism is challenging to handle without a proper schema, forcing type hierarchies to be implemented as structureless data labels.
Extend your data model continuously without refactoring or migrations
Extending a database often involves lengthy migration processes in which queries and application code have to be refactored. This involves reconciling the disparate structures between datasets and constructing a new data model that captures the polymorphism, leading to breaking changes in your code. Migration to a new model obsoletes existing queries, introduces new fields to existing objects, and changes the type signature of query results. TypeDB’s polymorphism enables you to extend your model without refactoring queries, migrating data, or modifying code.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
match
$user isa user, has email $email;
$rsrc isa! $resource-type, has id $id;
$resource-type sub resource;
$own (resource: $rsrc, owner: $user) isa resource-ownership;
get $email, $resource-type, $id;
TypeDB is a truly polymorphic database. Queries are written in a high-level declarative language and resolved against the schema at query-time. Variables implicitly match all valid types, so queries never have to be updated when new subtypes are added. Attributes and relations are always implemented in the same way, so cardinality changes never require a change to the underlying model. All this means that extensions to the data model are trivial, and don’t require refactors.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
SELECT
ue.email AS email,
"file" AS resource_type,
f.path AS id
FROM users u
JOIN user_emails ue
ON u.id = ue.user_id
JOIN resource_ownerships ro
ON u.id = ro.user_id
JOIN resources r
ON ro.resource_id = r.id
JOIN files f
ON r.id = f.resource_id
UNION ALL
SELECT
ue.email AS email,
"directory" AS resource_type,
d.path AS id
FROM users u
JOIN user_emails ue
ON u.id = ue.user_id
JOIN resource_ownerships ro
ON u.id = ro.user_id
JOIN resources r
ON ro.resource_id = r.id
JOIN directories d
ON r.id = d.resource_id
UNION ALL
SELECT
ue.email AS email,
"commit" AS resource_type,
c.hash AS id
FROM users u
JOIN user_emails ue
ON u.id = ue.user_id
JOIN resource_ownerships ro
ON u.id = ro.user_id
JOIN resources r
ON ro.resource_id = r.id
JOIN commits c
ON r.id = c.resource_id
UNION ALL
SELECT
ue.email AS email,
"repository" AS resource_type,
re.name AS id
FROM users u
JOIN user_emails ue
ON u.id = ue.user_id
JOIN resource_ownerships ro
ON u.id = ro.user_id
JOIN resources r
ON ro.resource_id = r.id
JOIN repositories re
ON r.id = re.resource_id
UNION ALL
SELECT
ue.email AS email,
"table" AS resource_type,
t.name AS id
FROM users u
JOIN user_emails ue
ON u.id = ue.user_id
JOIN resource_ownerships ro
ON u.id = ro.user_id
JOIN resources r
ON ro.resource_id = r.id
JOIN tables t
ON r.id = t.resource_id
UNION ALL
SELECT
ue.email AS email,
"database" AS resource_type,
f.name AS id
FROM users u
JOIN user_emails ue
ON u.id = ue.user_id
JOIN resource_ownerships ro
ON u.id = ro.user_id
JOIN resources r
ON ro.resource_id = r.id
JOIN databases d
ON r.id = d.resource_id;
In graph databases, properties are attached directly to the nodes that own them rather than being nodes themselves. Relocating attributes between nodes or reifying relations causes breaking changes to queries that rely on them. These changes to the logical data model and queries must be manually carried out due to the lack of a schema to define structure. Those model discrepancies can lead to data inconsistency and integrity issues.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
db.resource_ownerships.aggregate( [
{
$lookup:
{
from: "resources",
localField: "resource",
foreignField: "_id",
as: "resource"
}
},
{
$unwind:
{
path: "$resource"
}
},
{
$lookup:
{
from: "users",
localField: "owner",
foreignField: "_id",
as: "owner"
}
},
{
$unwind:
{
path: "$owner"
}
},
{
$unwind:
{
path: "$owner.emails"
}
},
{
$addFields:
{
resource_id: {
$switch: {
branches: [
{
case: {
$eq: ["$resource.resource_type", "file"]
},
then: "$resource.path"
},
{
case: {
$eq: ["$resource.resource_type", "directory"]
},
then: "$resource.path"
},
{
case: {
$eq: ["$resource.resource_type", "commit"]
},
then: "$resource.hash"
},
{
case: {
$eq: ["$resource.resource_type", "repository"]
},
then: "$resource.name"
},
{
case: {
$eq: ["$resource.resource_type", "table"]
},
then: "$resource.name"
},
{
case: {
$eq: ["$resource.resource_type", "database"]
},
then: "$resource.name"
}
]
}
}
}
},
{
$project: {
_id: false,
email: "$owner.emails",
resource_type: "$resource.resource_type",
id: "$resource_id"
}
}
] )
With document databases, the freeform structure makes inserting data trivial, but writing and validating queries a significant challenge. Migrating data relies on complete knowledge of its layout to ensure that no errors are introduced. When the layout of the data changes, any queries or code that rely on the previous structure stop working. The complex and brittle aggregation pipelines are particularly difficult to maintain and troubleshoot, especially when utilising lookups across multiple collections.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
MATCH
(user:User)-[:OWNS]->(rsrc:Resource)
WITH
rsrc,
[user.primary_email] + user.alias_emails AS emails,
labels(rsrc) AS resource_types,
keys(rsrc) AS properties
UNWIND emails AS email
UNWIND resource_types AS resource_type
WITH
rsrc, email, resource_type, properties,
{
File: "path",
Directory: "path",
Commit: "hash",
Repository: "name",
Table: "name",
Database: "name"
} AS id_type_map
WHERE resource_type IN keys(id_type_map)
AND id_type_map[resource_type] IN properties
RETURN email, resource_type, rsrc[id_type_map[resource_type]] AS id
In graph databases, properties are attached directly to the nodes that own them rather than being nodes themselves. Relocating attributes between nodes or reifying relations causes breaking changes to queries that rely on them. These changes to the logical data model and queries must be manually carried out due to the lack of a schema to define structure. Those model discrepancies can lead to data inconsistency and integrity issues.
Avoid data redundancy and ensure data consistency in real-time
Resolving complex dependencies in data requires intricate combinations of query patterns that are computationally intensive to run. Precomputing data is often the only practical solution. This leads to stale, inconsistent, and incorrect data, creating problems when real-time, accurate data is critical. TypeDB’s symbolic rules infer new data at query time, eliminating delay in synchronization. This ensures real-time access to consistent data backed by a single source of truth, avoiding data redundancy.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
define
rule transitive-team-membership:
when {
(team: $team-1, member: $team-2) isa team-membership;
(team: $team-2, member: $member) isa team-membership;
} then {
(team: $team-1, member: $member) isa team-membership;
};
match
$user isa user, has email $email;
$team isa team, has name $name;
(team: $team, member: $user) isa team-membership;
get $email, $name;
TypeDB’s built-in reasoning engine allows rules to be defined in the schema and resolved at query time. Rules are constructed using first-order logic and define how new facts can be inferred from existing data. When a query is issued, matching rules are triggered using the most recent data available. This allows all computed data to be built on a single source of truth, ensuring accuracy and consistency without delay or redundancy.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
CREATE PROCEDURE reload_inherited_team_memberships()
LANGUAGE SQL
BEGIN ATOMIC
TRUNCATE TABLE inherited_team_memberships;
WITH RECURSIVE parent_teams (team, member) AS (
SELECT team_memberships.team, team_memberships.member
FROM team_memberships
WHERE team_memberships.member IN (
SELECT id
FROM user
)
UNION ALL
SELECT team_memberships.team, team_memberships.member
FROM team_memberships
JOIN parent_teams
ON parent_teams.team = team_memberships.member
)
INSERT INTO inherited_team_memberships (team, member)
SELECT team, member
FROM parent_teams;
COMMIT;
END;
SELECT users.email AS email, teams.name AS name
FROM inherited_team_memberships
JOIN users
ON users.id = inherited_team_memberships.member
JOIN teams
ON teams.id = inherited_team_memberships.team;
SQL’s stored procedures enable computed data to be generated periodically. As modern data has grown in complexity, they have ceased to be performant enough for real-time use. Stale data accumulates as updates aren't immediately reflected, creating inconsistencies between the source and derived data. Scripts calling multiple procedures can even leave the data in an incorrect state due to propagating errors or race conditions.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
inherited_team_memberships = db.team_memberships.aggregate([
{
$lookup: {
from: "users",
localField: "member",
foreignField: "_id",
as: "member"
}
},
{
$unwind: {
path: "$member"
}
},
{
$graphLookup: {
from: "team_memberships",
startWith: "$team",
connectFromField: "team",
connectToField: "member",
as: "parent_teams"
}
},
{
$project: {
_id: false,
email: "$member.email",
team: {
$setUnion: [
[
"$team"
],
"$parent_teams.team"
]
}
}
},
{
$unwind: {
path: "$team"
}
},
{
$lookup: {
from: "teams",
localField: "team",
foreignField: "_id",
as: "team"
}
},
{
$unwind: {
path: "$team"
}
},
{
$project: {
email: "$email",
name: "$team.name"
}
}
])
db.inherited_team_memerships.remove({})
db.inherited_team_memberships.insertMany(inherited_team_memberships)
db.inherited_team_memberships.find({})
Document databases struggle to query efficiently when foreign document references are used to connect multiple collections. Lookup functions in aggregation pipelines require developers to explicitly identify and traverse every possible path between documents, forming a long and brittle pipeline. Missing or dissimilar fields in input documents silently generate bad data, requiring the pipeline to be painstakingly debugged stage-by-stage.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
MATCH
()<-[sub:SUBJECT]-(perm:InheritedPermission)-[obj:OBJECT]->(),
(perm)-[act:ACTION]->()
DELETE sub, obj, act, perm;
MATCH
(perm:Permission),
(user:User),
(rsrc:Resource),
(actn:Action)
WHERE (
(perm)-[:SUBJECT]->(user:User)
OR (perm)-[:SUBJECT]->(:Team)<-[:MEMBER_OF*]-(user:User)
) AND (
(perm)-[:OBJECT]->(rsrc:Resource)
OR (perm)-[:OBJECT]->(:Collection)<-[:MEMBER_OF*]-(rsrc:Resource)
) AND (
(perm)-[:ACTION]->(actn:Action)
OR (perm)-[:ACTION]->(:ActionSet)<-[:MEMBER_OF*]-(actn:Action)
) AND NOT EXISTS {
MATCH (user)<-[:SUBJECT]-(perm:InheritedPermission)-[:OBJECT]->(rsrc)
WHERE EXISTS ( (perm)-[:ACTION]->(actn) )
}
WITH user, rsrc, actn
CREATE (user)<-[:SUBJECT]-(perm:InheritedPermission)-[:OBJECT]->(rsrc)
WITH actn, perm
CREATE (perm)-[:ACTION]->(actn);
MATCH
(user:User)<-[:SUBJECT]-(perm:InheritedPermission)-[:OBJECT]->(rsrc:Resource),
(perm)-[:ACTION]->(actn:Action)
RETURN user, rsrc, actn;
Neo4j excels at identifying simple transitive relationships, but branching patterns are harder to express. This forces developers to specify all potential combinations of nodes, relationships, and properties, resulting in unmaintainable queries and poor performance. Because Neo4j lacks the expressive power of symbolic reasoning, these queries must be periodically executed to precompute the results, leading to stale and inconsistent data.
Build robust and elegant data architectures for complex applications
With strong typing, polymorphic queries, and a composable query language, TypeDB provides semantically meaningful data models and intuitive, powerful data access. It simplifies complex concept retrieval and enables new possibilities for challenging use cases.
Cybersecurity
Utilize expressive data modeling techniques to gain valuable insights into the intricate relationships between cyber-physical infrastructure, emerging threats, and sophisticated attacks.
Platform Engineering
Incorporate automation into developer workflows and facilitate the identification of deeply nested dependencies, all the while supporting ad hoc processes.
Knowledge Engineering
Use TypeDB’s conceptual schemas to model your knowledge domain without compromising on semantics, and harness the symbolic reasoning engine to easily generate new facts by deduction.
Virtual Representations
Develop a digital representation of complex environments and systems that encompass individual components, their interconnections, and dependencies.
Develop on TypeDB with a powerful set of developer tools
With a diverse ecosystem of tools, you can develop applications on TypeDB the way you want. Deploy servers locally or in the cloud, as single nodes or clusters, and connect with a range of asynchronous clients.
Start instantly
Run in a local Docker container or on AWS, GCP, and Azure with TypeDB Cloud, no dependencies/config needed.
Powerful tools
Manage databases, build and execute queries, and explore results with TypeDB Studio, a cross-platform IDE.
Robust APIs
Build complex and high-volume transactional applications with reactive and resilient open-source clients.
Connect with your choice of native language drivers
You have the flexibility to develop in your own way with a variety of officially supported open-source drivers.
Rustcargo add typedb-driver | Pythonpip install typedb-driver | Node.jsnpm install typedb-driver |
Javacom.vaticle.typedb:typedb-driver # from https://repo.vaticle.com/repository/maven/ | C#include "typedb-driver.h" | C++#include "typedb-driver.h" |
C#using TypedbDriver; | Goimport "vaticle.com/typedb-driver" |
Run fully managed databases in TypeDB Cloud
TypeDB Cloud lets you deploy secure, scalable, and highly available databases on demand, for development or production. With project and team administration, it’s easy to manage and monitor multiple deployments across your organization.
Global deployment, cloud agnosticDeploy in regions across AWS, GCP, and Azure (coming soon), with different databases in different cloud providers and geographies. | |
Scalability on demand, native clusteringConnect to clusters with topology-aware clients which can load balance requests across multiple replicas within a cluster. | |
Secure by default, roles and encryptionEnsure a high level of protection to safeguard data and maintain confidentiality with fully secured by default, with authentication as well as storage and network encryption. | |
Always available, automatic failoverGuarantee constant access to data thanks to synchronous replication and replicas which will automatically failover if the primary fails. | |
Teams and projects, organizational governanceCreate teams, members and projects to manage databases across the entire organization with ease. |
Join the global community of engineers developing with TypeDB
Open collaboration is a core value of the TypeDB community, and members actively discuss solutions on Discord and make contributions on GitHub. New faces are always welcome, and we hope to see you soon.
Learn why developers love TypeDB
TypeDB is a polymorphic database that empowers engineers to address intricate challenges that are beyond the capabilities of conventional SQL or NoSQL modeling paradigms. Find out what they’re building with it.
With its simple yet immensely powerful query language, native support for N-ary relationships and focus on semantic schema, TypeDB solves all our modeling problems so that we can focus more on solving higher level problems instead of tweaking traditional graph databases to fit our use cases.

Ram Anvesh

TypeDB enables us to model the supply chains of large Global CPG clients and identify environmental and social risks. We have found its strongly typed nature and expressivity to be world leading, allowing our customers to gain novel insights. With TypeDB, we can solve problems at the world's greatest companies.

Dixit Shah

For developers, TypeDB is really easy to work with. Its unique and expressive type system enables us to spend less time data modeling. We can easily integrate complex biomedical datasets. TypeDB provides us the backbone to our therapeutics platform to cure neurodegenerative diseases.

Nik Sharma

TypeDB's expressivity allows us to unify all levels of cyber intelligence for cyber security knowledge management systems. Through nested and hyper relations, we can easily represent TTPs and observables, attribution and victimology. During an attack, this gives analysts 360 views of any observable.

Samuel Hassine

TypeDB is a powerful framework for data exploration. The way data, attributes and relations can be expressed in a polymorphic manner allows us to build a rich network of multi-levels analysis and open opportunities to query, discover and infer interactions.

Jean-Paul Mochet

TypeDB provides a strongly-typed database with N-ary relations that enables modeling the world much closer to reality compared to other databases. In addition, its built-in inference engine enables to build next generation AI systems — one of the many reasons to choose TypeDB to model biomedical data.

Konrad Myśliwiec

TypeDB makes it easy for our robots to operate autonomously in the real world by being the centre of their understanding. TypeDB makes it easy to incorporate expert knowledge and advanced reasoning into its knowledge base.

Joris Sijs

Get started today
Deploy locally or in the cloud and start building now, or explore more about TypeDB and how its unique capabilities as a polymorphic database can refine and empower your applications.
Start building
Cloud or container, a polymorphic database with a conceptual data model, a strong subtyping system, a symbolic reasoning engine, and a type-theoretic language is minutes away.