-
Notifications
You must be signed in to change notification settings - Fork 0
Type Configuration
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.
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
.
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.
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()
.
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.
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.