Skip to content
mbroecheler edited this page Jun 6, 2012 · 5 revisions

In Titan, edge lables 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()
functional() Configures the type to be functional and acquires locks to ensure consistency Label and Key not functional TitanType.isFunctional()
functional(boolean) Configures the type to be functional. The boolean argument configures whether locks should be acquired when modifying an edge or property of this type Label and Key not functional TitanType.isFunctional()
simple() Configures the type to be simple, which means that the edges of this type do not allow properties to be set and invoking setProperty() on such an edge will throw an exception. Simple edges can be stored and retrieved more efficiently. Label not simple TitanType.isSimple()
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()
undirected() Configures the type for undirected edges, i.e. there is no order between out- and in-vertices Label directed TitanLabel.isUndirected()
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()
unique() Configures the key to be unique which means that each property value for this key is uniquely assigned to one vertex. For example, name is unique, because each name uniquely identifies one god. Acquires locks to ensure uniqueness and requires the key to be indexed. Key not unique TitanKey.isUnique()
indexed() Configures the key to be indexed which allows vertices to be retrieved by key-value pair via getVertices(key,value) 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.

Functional Types

A type is functional, if there is at most one edge or property of this type per vertex. Specifically, this means:

  • A property key is functional, if a vertex has at most one value associated with the key. name is an example of a functional property key since each god has one name.
  • An edge lable is functional, if a vertex has at most one outgoing edge for that label. father is an exmaple of a functional edge label, since each god has at most one father.

Since edges and properties of functional labels and keys must be unique per vertex, inconsistencies could arise when two TitanGraph instances try to update the same functional edge or property concurrently, since one may overwrite the change of the other. To avoid such inconsistencies, Titan will acquire locks on functional 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 functional TitanType can be configured to not acquire locks using TypeMaker.functional(false). This configuration options 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 and 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).functional().makePropertyKey();
TitanLabel battled = g.makeType().name("battled").primaryKey(time).makeEdgeLabel();

In this example, the functional 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 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 either functional property keys or functional, simple, unidirected edge lables.

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).functional().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 functional property keys or functional, simple, unidirected edge lables.

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 functional 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.

Clone this wiki locally