Type Definition Overview

In Titan, edge labels and property keys are types which can be individually configured to provide data verification, better storage efficiency, and higher performance. Types are uniquely identified by their name and are themselves vertices in the graph. Type vertices can be retrieved by their name.

TitanGraph graph = ...
TitanType name = graph.getType("name");

A TitanType is either a TitanLabel (for edges) or a TitanKey (for properties) which means either TitanType.isEdgeLabel() or TitanType.isPropertyKey() is true and we can cast it to the particular subtype.

TitanType name = graph.getType("name");
if (name.isPropertyKey()) 
  TitanKey namekey = (TitanKey)name;
else 
  TitanLabel namelabel = (TitanLabel)name;

Most methods in Titan are overloaded to allow either the type name or the type object as argument.

Type Creation

Labels and keys are automatically created when their name is first used. However, types can also be created and configured explicitly by the user through a TypeMaker instance returned by TitanGraph.makeType(). The TypeMaker provides the following type configuration options.

Method Description Applies to Default Inspection Method
name(String) Defines the name of the type. Must be unique across all types. Required. Label and Key TitanType.getName()
unique(Direction) Configures the type to be unique in the given direction and acquires locks to ensure consistency Label and Key not unique TitanType.isUnique(Direction)
unique(Direction, lock) Configures the type to be unique in the given direction with the specified consistency guarantee (either LOCK or NO_LOCK). Label and Key not unique TitanType.isUnique(Direction)
group(TypeGroup) Assigns the type to the specified TypeGroup which allows grouping of edges for efficient retrieval. Label and Key DEFAULT_GROUP TitanType.getGroup()
directed() Configures the type for directed edges, i.e. from out-vertex to in-vertex. Label directed TitanLabel.isDirected()
unidirected() Configures the type for unidirected edges, i.e. edges that can only be traversed from out-vertex to in-vertex. Unidirected edges can be stored more efficiently Label directed TitanLabel.isUnidirected()
indexed(Element) Configures the key to be indexed for the given element type using the default index which allows elements of that type to be retrieved using graph.query() Key not indexed TitanKey.hasIndex()
indexed(Element,String) Configures the key to be indexed for the given element type using the specified index (identified by name) which allows elements of that type to be retrieved using graph.query() Key not indexed TitanKey.hasIndex()
dataType(Class) Configures the data type of this key. Property instances for this key will only accept attribute values that are instances of this class. Every property key must have its data type configured. Setting the data type to Object.class allows any type of attribute but comes at the expense of longer serialization because class information
is stored with the attribute value. See Graph Configuration for more information on how to define custom attribute data types.
Key TitanKey.getDataType()
primaryKey(TitanType...) Configures the composite primary key for this type. Label empty
signature(TitanType...) Configures the signature of this type. Label empty
makeEdgeLabel() Creates an edge label according to the configuration of this TypeMaker. Label
makePropertyKey() Creates a property key according to the configuration of this TypeMaker. Key

Below are some examples of creating labels and keys using TypeMaker.

Unique Types

A type is unique, if there is at most one edge or property of this type per vertex in the given direction. Specifically, this means:

  • A property key is out-unique, if a vertex has at most one value associated with the key. name is an example of an out-unique property key since each god has one name.
  • An edge label is out-unique, if a vertex has at most one outgoing edge for that label. father is an example of an out-unique edge label, since each god has at most one father.
  • A property key is in-unique, if there is at most one vertex which has any given attribute as its property value. ssn is an example of a in-unique property key since each social security number is uniquely associated with one person.

Since edges and properties of unique labels and keys must be unique per vertex, inconsistencies could arise when two TitanGraph instances try to update the same unique edge or property concurrently, since one may overwrite the change of the other. To avoid such inconsistencies, Titan will acquire locks on unique edges and properties by default. Acquiring locks, however, can be very expensive depending on the storage backend. In cases where concurrent modifications can be excluded or blind overwrites are acceptable, a unique TitanType can be configured to not acquire locks by passing in UniquenessConsistency.NO_LOCK as a second argument to TypeMaker.unique(). This configuration option should be used with care and only if the extra performance gain is needed.

Type Groups

Titan allows types to be grouped and to retrieve all edges or properties for a type group. For example, in the graph of gods we have father, mother, and brother labels. If we want to retrieve all family members of a vertex, we could do

v.getVertices(OUT,"father","mother","brother")

However, this becomes cumbersome as more family labels like sister, uncle, etc are added. Moreover, each edge label requires an independent index retrieval. In the example above, the database index is accessed three times. Instead, a family type group can group all these labels together.

TypeGroup family = TypeGroup.of(2,"family");
TitanLabel father = g.makeType().name("father").group(family).makeEdgeLabel();
TitanLabel mother = g.makeType().name("mother").group(family).makeEdgeLabel();
TitanLabel brother = g.makeType().name("brother").group(family).makeEdgeLabel();
// Load data...

TitanVertex jupiter = (TitanVertex)g.getVertices("name","jupiter").iterator().next();
jupiter.query().group(family).vertices();

In this example, a family type group is defined with id 2. Type groups are uniquely identified by their id and NOT their name. That is, two type groups with the same id are considered equivalent. TypeGroup.DEFAULT_GROUP has id=1 and therefore custom type groups should use ids larger than 1. The maximum id is 126.
Once the type group is defined, we assign the father, mother, and brother labels to this group. Now, all family members of Jupiter can be retrieved in one database operation by specifying the group via TitanQuery.group().

Primary Keys and Signatures

Specifying the primary key of a labels allows edges with this label to be efficiently retrieved in the order of the key. Titan builds vertex-centric indexes for each label according to the primary key definition which can significantly speed up queries.

TitanKey time = g.makeType().name("time").dataType(Integer.class).unique(OUT).makePropertyKey();
TitanLabel battled = g.makeType().name("battled").primaryKey(time).makeEdgeLabel();

In this example, the out-unique property key time is defined with data type Integer. This property key is then used as the primary key for the battled edge label. Hence, battled edges will be sorted by time in ascending order and battles that happened in a certain time range can be queried for more efficiently. Moreover, battled edges are stored more compactly on disk.

Note, that TitanTypes used in the primary key must be out-unique property keys or out-unique, unidirected edge labels.

If one is not interested in configuring the order of edges but only wants to benefit from the storage efficiencies introduced by primary keys, one can alternatively configure the signature of a label. Specifying the signature of a label tells the graph database to expect that edges with this label always have or are likely to have an incident property or unidirected edge of the type included in the signature. This allows the graph database to store such edges more compactly and retrieve them more quickly.

TitanKey time = g.makeType().name("time").dataType(Integer.class).unique(OUT).makePropertyKey();
TitanLabel battled = g.makeType().name("battled").signature(time).makeEdgeLabel();

This example is almost identical to the primary key example above with the only difference that time is configured to be part of the signature.

If a type is used in the primary key, it cannot be part of the signature.
As before, TitanTypes used in the signature must be either out-unique property keys or out-unique, unidirected edge labels.

Default Type Creation

Titan will create edge labels and property keys the first time they are referenced by name using a default configuration unless they have been previously configured using TypeMaker as discussed above.

By default, property keys are configured to be out-unique but non-locking with Object.class as the data type. Note, that it is more efficient to define an appropriate data type via TypeMaker. Hence, property keys don’t have an index by default. To create an indexed key with this default configuration, invoke Graph.createKeyIndex("name",Vertex.class) before the property key is being used.

Edge labels are created according to the default configuration shown in the table above.

The default type creation behavior is configured via the autotype configuration option. By default, it uses the configuration value blueprints which creates types automatically as described above. To disable automatic type creation, set autotype=none. Setting the option to none requires that all types are explicitly created and will throw an IllegalArgumentException each time a non-existent type is referenced which is useful to avoid type name typos.

Last edited by Dan LaRocque, 2013-07-28 06:49:42