Schema Reference
TerminusDB schema language based on simple JSON syntax.
The TerminusDB schema language enables documents and their relationships to be specified using simple JSON syntax. This syntax makes it as easy as possible to specify a JSON object to automatically convert to a graph. This approach enables data to be viewed as collections of documents or as knowledge graphs of interconnected objects.
A JSON object in TerminusDB schema is composed of key-value pairs.
A key is one of two values, keyword or property, described in the table below. The full schema definition is a stream or list of these values or JSON objects.
Key type | Example | Description |
---|---|---|
keyword | @id | Starts with @ , has a value with a special meaning. |
property | name | Does not start with @ , has a value with a range type. |
The basic unit of specification is a class. A class definition is a schema object with the keyword
@type
with type value Class
. The keyword @id
specifies the name of the class. The example below define a class named Person
with a property name
of type xsd:string
. Search XSD definitions for more information about types.{
"@type" : "Class",
"@id" : "Person",
"name" : "xsd:string"
}
The context object is a special schema object affecting the entire schema. The context object is specified by the special
@type
value @context
. An example:{ "@type" : "@context",
"@schema" : "http://terminusdb.com/schema/woql#",
"@base" : "terminusdb://woql/data/",
"xsd" : "http://www.w3.org/2001/XMLSchema#",
"@documentation" :
{
"@title" : "WOQL schema",
"@authors" : ["Gavin Mendel-Gleason"],
"@description" : "The WOQL schema providing a complete specification of the WOQL syntax.
This enables:
* WOQL queries to be checked for syntactic correctness.
* Storage and retrieval of queries.
* Queries to be associated with data products.
* Helps to prevent errors and detect conflicts in merge of queries.",
}
}
This example does the following:
- Defines default prefixes in
@schema
and@base
to use for the schema and data. - Defines the prefix
xsd
enabling vocabulary based on different URL prefixes.- For example, specify
xsd:string
to denotehttp://www.w3.org/2001/XMLSchema#string
- Documents the schema in the
@documentation
value, providing:@title
@authors
@description
All properties in the context object that do not start with
@
, such as xsd
, are URI definitions. They must be of the form shown below. Prefix and URI are defined by their respective regular expressions. That is, a prefix has an identifier starting with an alphabetic character followed by alphanumeric characters. The URI has a protocol followed by valid URI characters. Each prefix is paired with a URI.Prefix := ":alpha::alphaNum:*"
URI := ":alpha:alphaNum:*://:uriChar:*"
{ ...
Prefix : URI
... }
A list of keywords used in the context object.
The
@schema
keyword specifies the default URI expansion to use for all elements of the schema. In the example below, the class name NamedQuery
expands to http://terminusdb.com/schema/woql#NamedQuery
.{
"@type" : "@context",
"@schema" : "http://terminusdb.com/schema/woql#",
"@base" : "terminusdb://woql/data/"
}
{
"@id" : "NamedQuery",
"@type" : "Class",
"@key" :
{
"@type" : "Lexical",
"@fields" : [ "name" ]
},
"name" : "xsd:string",
"query" : "Query"
}
@base
specifies the default URI expansion used for all elements of instance data. In the previous schema definition, and given the document in the instance graph example below, the id NamedQuery_my_query
expands to terminusdb://woql/data/NamedQuery_my_query
.{
"@type" : "NamedQuery",
"@id" : "NamedQuery_my_query",
"name" : "my_query",
"query" :
{
"@type" : "True"
}
}
@documentation
specifies documentation global to the entire schema. See the @documentation
section in the previous context object example. The @documentation
tag can be a single value, or it can be a list with each element having an additional @langugage
tag. The @language
tag must have an IANA language code, and this will be used to select appropriate descriptions when internationalising the schema.The documentation section contains the keywords:
The
@title
of the schema to display.A long-form
@description
of the purpose of the schema, the type of documents contained in the schema, and keywords useful for searching for the type of content that the schema encodes.A list of strings of
@authors
involved in writing the schema.If you would like to add arbitrary JSON structured metadata to a schema, you can place it in the
@metadata
field of the context object. This can be used to store data product-wide information in a structured format. For instance:{ "@type" : "@context",
"@base" : "http://my_stuff/",
"@schema" : "http://my_schema#",
"@metadata" : { "configuration" : { "frob" : 29 } }
}
If you use the
@language
code, specific documentation results can appear in different circumstances depending on the users language preferences.An example of the
@language
tag for a context is as follows:{ "@base": "terminusdb:///data/",
"@schema": "terminusdb:///schema#",
"@type": "@context",
"@documentation" : [{
"@language" : "en",
"@title" : "Example Schema",
"@description" : "This is an example schema. We are using it to demonstrate the ability to display information in multiple languages about the same semantic content.",
"@authors" : ["Gavin Mendel-Gleason"]
},
{ "@language" : "ka",
"@title" : "მაგალითი სქემა",
"@description" : "ეს არის მაგალითის სქემა. ჩვენ ვიყენებთ მას, რათა ვაჩვენოთ ინფორმაციის მრავალ ენაზე ჩვენების შესაძლებლობა ერთი და იმავე სემანტიკური შინაარსის შესახებ.",
"@authors" : ["გავინ მენდელ-გლისონი"]
}
],
"xsd" : "http://www.w3.org/2001/XMLSchema#"
}
A document definition includes several properties, and the keywords, prefixed
@
, describing class behavior.The
@type
of the object. At the schema level, this is one of: Enum
, Class
, TaggedUnion
and Unit
.If you would like to add arbitrary JSON structured metadata to a class, you can place it in the
@metadata
field of the class. This can be used to direct various approaches to display of the class, or associated information for backend or front-ends which may have different requirements. It is generally good practice to keep important metadata one level deeper in a JSON object so as to leave space for other kinds of metadata. For instance:{ "@type" : "Class",
"@id" : "MyClass",
"@metadata" : { "display_format" : { "colour" : "Blue", "size" : [100,400]}},
"name" : "xsd:string" }
The three varieties of document are described below:
Class
designates a standard class document. It contains the definition of several properties and keywords describing various class attributes. An example of a class, and an instance of the class:{
"@id" : "Dog",
"@type" : "Class",
"@base" : "Dog_",
"@key" :
{
"@type" : "Lexical",
"@fields" : [ "name" ]
},
"name" : "xsd:string",
"hair_colour" : "Colour"
}
{
"@type" : "@context",
"@base" : "http://i/",
"@schema" : "http://s#"
}
{
"@type" : "Dog",
"@id" : "Dog_Cerberus",
"name" : "Cerberus",
"hair_colour" : "Grey"
}
An
Enum
is a non-standard class in which each instance is a simple URI with no additional structure. To be a member of the class, you must be one of the referent URIs. An Enum
example with an extension Blue
is s shown below. In the database, the actual URI for an Enum is expanded with the preceding type name, so the Blue
extension becomes http://s#PrimaryColour/Blue
{
"@type" : "Enum",
"@id" : "PrimaryColour",
"@value" :
[
"Red",
"Blue",
"Yellow"
]
}
"Blue"
A
TaggedUnion
specifies mutually exclusive properties. This is useful when there is a disjoint choice between options.Examples below of a schema with a TaggedUnion and a concrete TaggedUnion class extension. In these examples, the
BinaryTree
class specifies a TaggedUnion
enabling a choice between a leaf
(with no value), or a node
class with a value and branches.{
"@type" : "@context",
"@base" : "http://i/",
"@schema" : "http://s#"
}
{
"@id" : "BinaryTree",
"@type" : "TaggedUnion",
"@base" : "binary_tree_",
"@key" :
{
"@type" : "ValueHash"
},
"leaf" : "sys:Unit",
"node" : "Node"
}
{
"@id" : "Node",
"@type" : "Class",
"@key" :
{
"@type" : "ValueHash"
},
"value" : "xsd:integer",
"left" : "BinaryTree",
"right" : "BinaryTree"
}
{
"@type" : "Node",
"value" : 0,
"left" :
{
"@type" : "BinaryTree",
"leaf" : []
},
"right":
{
"@type" : "BinaryTree",
"leaf" : []
}
}
The
TaggedUnion
is a special case and syntactic sugar for the more general case of collections of disjoint properties. These more complex cases can be represented by inheriting from a number of TaggedUnion
s, but they may also be given explicitly using the @oneOf
field, together with a Class.The value of the
@oneOf
field is a set, so can be any number of documents all of which have mutually disjoint properties, but which can coexist. Examples with more than one disjoint property are given below.{
"@type" : "@context",
"@base" : "http://i/",
"@schema" : "http://s#"
}
{
"@id" : "IntOrString",
"@type" : "Class",
"@oneOf" :
{
"integer": "xsd:integer",
"string" : "xsd:string"
}
}
{
"@id" : "Friend",
"@type" : "Class",
"@key" :
{
"@type" : "Lexical",
"@fields": ["name"]
},
"name" : "xsd:string"
}
{
"@id" : "Toy",
"@type" : "Class",
"@key" :
{
"@type" : "Lexical",
"@fields": ["name"]
},
"name" : "xsd:string"
}
{
"@id" : "Pet",
"@type" : "Class",
"name" : "xsd:string",
"@oneOf" : [
{
"cat" : "Toy",
"dog" : "Friend"
},
{
"employers" : "xsd:positiveInteger",
"unemployed": "xsd:string"
},
]
}
{
"@type" : "IntOrString",
"integer" : 0
}
{
"@type" : "IntOrString",
"string" : "zero"
}
{
"@type" : "Pet",
"cat" : {
"@type": "Toy",
"name" : "ball of string"
},
"employers": 5
}
{
"@type" : "Pet",
"dog" : {
"@type": "Person",
"name" : "Jim"
},
"unemployed": "A house pet."
}
{
"@type" : "Pet",
"string" : "zero"
}
But not:
{
"@type" : "IntOrString",
"integer" : 0,
"string" : "zero"
}
The
Unit
type has a single extension []
. This is used when only the presence of the property is interesting, but it has no interesting value. See the BinaryTree
in the TaggedUnion class extension example above.The
@id
key of a class defines the class name and identifier. The name uniquely defines the class, enabling the class to be updated, retrieved, and deleted. In the example below, the class is named NamedQuery
. It does not have a fully qualified URL or prefix, so it is implicitly based on the URI given for @schema
.{
"@id" : "NamedQuery",
"@type" : "Class",
"@key" :
{
"@type" : "Lexical",
"@fields" : [ "name" ]
},
"name" : "xsd:string",
"query" : "Query"
}
@key
specifies the mechanism to define the @id
of documents in the database, similar to a primary key in relational database terms. Valid key types are Lexical
, Hash
, ValueHash
, Random
.If the key
@base
is specified in the class, then this is pre-pended to the key. If this is a fully qualified URI then it is complete, otherwise, it is combined with the value of @base
from the context.A
Lexical
key specifies a URI name formed from a URI encoded combination of all @fields
arguments provided, in the order provided. An example is shown below. With this key type (or key strategy) a URI is formed from the combination of first_name
and last_name
. If @base
is specified in the class, this is prepended.Given the simple document definition below, this will either generate (if
@id
is not supplied) or check that the URI http://example.com/people/Person_Hasdrupal_Barca
is the @id
element.{
"@type" : "@context",
"@schema" : "http://example.com/people#",
"@base" : "http://example.com/people/"
}
{
"@id" : "Person",
"@type" : "Class",
"@base" : "Person_",
"@key" :
{
"@type" : "Lexical",
"@fields" :
[
"first_name",
"last_name"
]
},
"first_name" : "xsd:string",
"last_name" : "xsd:string",
"year_of_birth" : "xsd:gYear"
}
{
"@type" : "Person",
"first_name" : "Hasdrupal",
"last_name" : "Barca",
"year_of_birth" : "-245"
}
Hash
is generated in the same way as Lexical
except that values are first hashed using the SHA-256 hash algorithm.Use this where there:
- Are numerous items that form the key making the URI unwieldy.
- Is no need for the URI to inform the user of the content of the object.
- Is a requirement that data about the object is not be revealed by the key.
Define a
Hash
in the same way as the Lexical key strategy example in the previous section, replacing the @key
@type
value from Lexical
to Hash
.Given the simple document definition in the previous section, the
@id
Person_5dd7004081e437b3e684075fa3132542f5cd06c1
is generated.The
ValueHash
key generates a key defined as the downward transitive closure of the directed acyclic graph from the root of the document. This means you can produce a key that is entirely based on the entire data object. Note ValueHash
:- Takes no additional keywords.
- Objects must be directed acyclic graphs, they cannot be cyclic.
In the example below,
ValueHash
is formed only from the value of layer:identifier
.{
"@id" : "layer:Layer",
"@type" : "Class",
"@documentation" :
{
"@comment" : "A layer object which has the identifier used in storage.",
"@properties" :
{
"layer:identifier" : "The layer identifier."
}
},
"@base" : "layer_data:Layer_",
"@key" :
{
"@type" : "ValueHash"
},
"layer:identifier" : "xsd:string"
}
Use
Random
as a convenient key type when an object has no important characteristics that inform a key or does not need to be constructed such that it is reproducible. In the example below, the @key
@type
is defined as Random
, meaning each new database that is added is unique regardless of label.{
"@id" : "UserDatabase",
"@type" : "Class",
"@documentation" :
{
"@comment" : "A normal user database.",
"@properties" :
{
"label" : "The label name of the database.",
"comment" : "A comment associated with the database.",
"creation_date" : "The time of creation of the database.",
"state" : "The system transaction state of the database."
}
},
"@inherits" : "Database",
"@key" :
{
"@type" : "Random"
},
"label" : "xsd:string",
"comment" : "xsd:string",
"creation_date" : "xsd:dateTime",
"state" : "DatabaseState"
}
Use
@documentation
to add documentation to the class and the property fields or values of the class. The @documentation
can either be an object, or a list of objects with specified languages (and at most one default unspecified). An example using multiple languages might be:{ "@id" : "Example",
"@type" : "Class",
"@documentation" : [
{
"@label" : "Example",
"@comment" : "An example class",
"@properties" : { "name" : { "@label" : "name",
"@comment" : "The name of the example object" },
"choice" : { "@label" : "choice",
"@comment" : "A thing to choose" }}
},
{
"@language" : "ka",
"@label" : "მაგალითი",
"@comment" : "მაგალითი კლასი",
"@properties" : { "name" : { "@label" : "სახელი",
"@comment" : "მაგალითის ობიექტის სახელი" },
"choice" : { "@label" : "არჩევანი",
"@comment" : "რაც უნდა აირჩიოთ" }}
}
],
"name" : "xsd:string"
}
The keywords of the
@documentation
object are @comment
and either @properties
or @values
for standard classes or Enums
respectively. Each of the @properties
or @values
can likewise have either a simple label, or an object with @label
and @comment (as above)
.For
Enum
we can write as follows:{
"@id": "Pet",
"@type": "Enum",
"@documentation" : {
"@comment" : "What kind of pet?",
"@values" : {
"dog" : "A doggie",
"cat" : "A kitty"
}
},
"@value" : ["dog","cat"]
}
For a standard
Class
with one default language, we can write as follows:{
"@id": "Person",
"@type": "Class",
"@documentation" : {
"@comment" : "Information about people",
"@values" : {
"name" : "The persons name",
"friends" : "The kinds of company someone keeps"
}
},
"name" : "xsd:string",
"friends" : {
"@type" : "Set",
"@class" : "Person"
}
}
The
@comment
is the class description.The
@properties
keyword is a JSON object with pairs of the form:{
"property_1" : "description_1",
...
"property_n" : "description_n"
}
or with properties pointingn to JSON objects, as:
{
"property_1" : { "@label" : "description_1", "@comment" : "comment_1" },
...
"property_n" : { "@label" : "description_2", "@comment" : "comment_2" }
}
@base
specifies a prefix to prepare to the @key
. This prefix is absolute if @base
is a fully qualified URI, otherwise, it will, in turn, be prefixed by the system-wide @base
definition. In the example below, the @base
for the class is fully qualified after the layer_data
prefix is expanded. This means the layer URIs have the form terminusdb://layer/data/Layer_
followed by a random string.{
"@type" : "@context",
"@documentation" :
{
"@title" : "The Ref schema",
"@description" : "This is the Ref schema. It gives a specification for storage of references, branches and commits in our commit graph.",
"@authors" :
[
"Gavin Mendel-Gleason",
"Matthijs van Otterdijk"
]
},
"@base" : "terminusdb://ref/data/",
"@schema" : "http://terminusdb.com/schema/ref#",
"layer" : "http://terminusdb.com/schema/layer#",
"layer_data" : "terminusdb://layer/data/",
"xsd" : "http://www.w3.org/2001/XMLSchema#"
}
{
"@id" : "layer:Layer",
"@type" : "Class",
"@documentation" :
{
"@comment" : "A layer object which has the identifier used in storage.",
"@properties" :
{
"layer:identifier" : "The layer identifier."
}
},
"@base" : "layer_data:Layer_",
"@key" :
{
"@type" : "ValueHash"
},
"layer:identifier" : "xsd:string"
}
The
@subdocument
key is present with the value []
or it is not present.A class designated as a sub-document is considered to be completely owned by its containing document. It is not possible to directly update or delete a subdocument, but it must be done through the containing document. Currently, subdocuments must have a key that is
Random
or ValueHash
(this restriction may be relaxed in the future.)See below for examples of a subdocument declaration in a schema, and a corresponding subdocument.
{
"@type" : "@context",
"@base" : "terminusdb://i/",
"@schema" : "terminusdb://s#"
}
{
"@type" : "Class",
"@id" : "Person",
"age" : "xsd:integer",
"name" : "xsd:string",
"address" : "Address"
}
{
"@type" : "Class",
"@id" : "Address",
"@key" :
{
"@type" : "Random"
},
"@subdocument" : [],
"country" : "xsd:string",
"postal_code" : "xsd:string",
"street" : "xsd:string"
}
{
"@type" : "Person",
"@id" : "doug",
"name" : "Doug A. Trench",
"address" :
{
"@type" : "Address",
"country" : "Neverlandistan",
"postal_code" : "3",
"street" : "Cool Harbour lane"
}
}
The
@abstract
key is present with the value []
or it is not present.An abstract class has no concrete referents. It provides a common superclass and potentially several properties shared by all of its descendants. Create useful concrete members using the
@inherits
keyword.An example of the abstract keyword in a schema, and a concrete instance of the
Person
class, but not of the NamedEntity
class:{
"@type" : "@context",
"@base" : "terminusdb://i/",
"@schema" : "terminusdb://s#"
}
{
"@type" : "Class",
"@abstract" : [],
"@id" : "NamedEntity",
"name" : "xsd:string"
}
{
"@type" : "Person",
"@id" : "Person",
"@inherits" : ["NamedEntity"]
}
{
"@type" : "Person",
"@id" : "doug",
"name" : "Doug A. Trench"
}
@inherits
enables classes to inherit properties (and the @subdocument
designation) from parent classes. It does not inherit key strategies.This inheritance tree is also available as a
subsumption
relation in the WOQL query language and provides semantics for frames in the schema API.The range of
@inherits
can be a class or a list of classes. For example:{
...,
"@inherits" : "MyClass",
...
}
Or
{
...,
"@inherits" :
[
"MyFirstClass", "MySecondClass"
]
...
}
Multiple inheritance is allowed as long as all inherited properties of the same name have the same range class. If range classes conflict, the schema check fails.
An example of inheritance of properties and an object meeting this specification:
{
"@type" : "@context",
"@base" : "http://i/",
"@schema" : "http://s/"
}
{
"@id" : "RightHanded",
"@type" : "Class",
"right_hand" : "xsd:string"
}
{
"@id" : "LeftHanded",
"@type" : "Class",
"left_hand" : "xsd:string"
}
{
"@id" : "TwoHanded",
"@type" : "Class",
"@inherits" :
[
"RightHanded", "LeftHanded"
]
}
{
"@type" : "TwoHanded",
"@id" : "a two-hander",
"left_hand" : "Pretty sinister",
"right_hand" : "But this one is dexterous"
}
The
@unfoldable
key is present with the value []
or it is not present.In the document API, when retrieving documents, the default behavior is for any linked document to be returned as an IRI, while subdocuments are fully unfolded and returned as a nested document. With the
@unfoldable
option set, linked documents will behave just like subdocuments, and will also be unfolded on retrieval.The
@unfoldable
option can only be set on a class which does not directly or indirectly link to itself. This prevents a self-referencing document from being unfolded infinitely.The purpose of
@unfoldable
is to be able to treat linked (top-level) documents as subdocuments in representation. Subdocuments can only be linked by one document, its owner, whereas normal documents can be linked by any number of other documents. If the desired result is to have a document linked by several other documents, but still have it fully unfolded on retrieval like a subdocument, use this option.{
"@type" : "@context",
"@base" : "terminusdb://i/",
"@schema" : "terminusdb://s#"
}
{
"@type" : "Class",
"@id" : "Person",
"name" : "xsd:string",
"address" : "Address"