Graph

This section exposes the APIs available through the DataStax Java driver for interacting with a DataStax Enterprise Graph Server.

Note that, as an alternative to those native APIs, the driver also provides an integration with Apache Tinkerpop.

This documentation is divided into the following sections:

Executing a query

DseSession has dedicated methods to execute graph queries:

import com.datastax.driver.dse.graph.GraphStatement;
import com.datastax.driver.dse.graph.SimpleGraphStatement;

dseSession.executeGraph("system.graph('demo').ifNotExists().create()");

GraphStatement s1 = new SimpleGraphStatement("g.addV(label, 'test_vertex')").setGraphName("demo");
dseSession.executeGraph(s1);

GraphStatement s2 = new SimpleGraphStatement("g.V()").setGraphName("demo");
GraphResultSet rs = dseSession.executeGraph(s2);
System.out.println(rs.one().asVertex());

Note: you need to set schema_mode: Development in dse.yaml to run the example above.

A graph query can be executed either via the executeGraph() method that accepts a string query in parameter, or the executeGraph() method that accepts a GraphStatement. A simple implementation of a GraphStatement is the SimpleGraphStatement, which accepts a query string, and allows setting specific graph options on the statement instance.

Asynchronous execution

Graph queries and statements can also be executed asynchronously via the executeGraphAsync() methods.

Handling results

Graph queries return a GraphResultSet, which is essentially an iterable of GraphNode:

GraphResultSet rs = dseSession.executeGraph("g.V()");

// Iterating:
for (GraphNode n : rs) {
    System.out.println(n);
}

// Get the first result only (or if you know there is exactly one):
GraphNode n = rs.one();

GraphNode wraps the responses returned by the server. You can coerce the result to a specific type using the asXxx() methods:

GraphNode n = dseSession.executeGraph("g.V().count()").one();
System.out.printf("The graph has %s vertices%n", n.asInt());

If the result is an array or an object (non-leaf node), you can iterate its child elements:

if (n.isArray()) {
    for (int i = 0; i < n.size(); i++) {
        GraphNode child = n.get(i);
        System.out.printf("Element at position %d: %s%n", i, child);
    }
}

if (n.isObject()) {
    Iterator<String> fieldNames = n.fieldNames();
    while (fieldNames.hasNext()) {
        String fieldName = fieldNames.next();
        System.out.printf("Element at key %s: %s%n", fieldName, n.get(fieldName));
    }
}

The driver also exposes general-purpose methods to handle results in the form of Maps and Lists:

GraphNode n = dseSession.executeGraph("g.V().valueMap()").one();
Map<String, Object> values = n.asMap();

Graph structural types

The driver has client-side representations for Vertex, Edge, Path, VertexProperty, and Property.

These are accessible via the corresponding GraphNode#asXXXX() methods:

GraphNode n = dseSession.executeGraph("g.V().hasLabel('test_vertex')").one();
Vertex vertex = n.asVertex();

n = dseSession.executeGraph("g.V().hasLabel('test_vertex').outE()").one();
Edge edge = n.asEdge();

n = dseSession.executeGraph("g.V().hasLabel('test_vertex').outE().inV().path()").one();
Path path = n.asPath();

n = dseSession.executeGraph("g.V().hasLabel('test_vertex').next().property('propName')").one();
VertexProperty vertexProperty = n.asVertexProperty();

Data types compatibility matrix

DSE Graph exposes several data types when defining a schema for a graph.

Those data types server-side translate into specific Java classes when the data is returned from the server.

Here is the exhaustive list of possible DSE Graph data types, and their corresponding classes in the Java driver:

DSE Graph Java Driver
bigint Long
int Integer
double Double
float Float
uuid UUID
bigdecimal BigDecimal
duration java.time.Duration
inet InetAddress
timestamp java.time.Instant
time java.time.LocalTime
date java.time.LocalDate
smallint Short
varint BigInteger
polygon Polygon
point Point
linestring LineString
blob byte[]

Deserializing complex data types

The driver exposes methods to deserialize data into more complex data types, as long as the server-side data type associated corresponds. Doing so requires using the GraphNode#as(Class<T> clazz) method:

GraphNode n = dseSession.executeGraph("g.V().hasLabel('test_vertex')").one();
Vertex vertex = n.asVertex();
UUID uuidProp = vertex.getProperty("uuidProp").getValue().as(UUID.class);

Java 8 Time types

The DSE Java driver is compatible with Java from version 6. The driver is able to automatically determine whether the application it’s used with is running on such a legacy Java version, and will be able to deserialize objects differently accordingly.

If using the driver with Java version 8 or higher, java.time types will be usable when retrieving the data from a Traversal (see the types matrix above).

If the driver is used with an inferior version of Java, other classes will be usable when retrieving the data sent by the server. For Java versions < 8, the Duration() and Time() DSE Graph types will be exposed as Strings. Timestamp() will be exposed as a java.util.Date, and Date() as a com.datastax.driver.core.LocalDate.

A word on Properties

The vertex properties exposed by the driver (VertexProperty) respect the same behaviour as in Apache TinkerPop. First, a VertexProperty is a property, with a simple value. But in addition to that, a VertexProperty can be the parent of a sublist of Property. If that’s the case, the sub properties of VertexProperty are called meta-properties.

Moreover, in a vertex the same property key can be associated to multiple vertex properties. In this case, the vertex is said to have “multi-properties”.

Here is the syntax for dealing with vertex properties with the DataStax Java driver graph types:

GraphNode n = dseSession.executeGraph("g.V().hasLabel('test_vertex_props')").one();
Vertex vertex = n.asVertex();

// there can be more than one VertexProperty with the key "multi_with_meta_props"
Iterator<VertexProperty> vertexProps = vertex.getProperties("multi_with_meta_props");

VertexProperty vertexProp1 = vertexProps.next();
// the value of the vertex property
int vertexProp1Value = vertexProp1.getValue().asInt();
// the meta-properties of the vertex property
Iterator<Property> metaProps1 = vertexProp1.getProperties();
Property metaProp11 = metaProps1.next();
double metaPropValue11 = metaProp11.getValue().asDouble(); 
Property metaProp12 = metaProps1.next();
double metaPropValue12 = metaProp12.getValue().asDouble(); 

// **multi-properties**.
VertexProperty vertexProp2 = vertexProps.next();
[...]

More on how to create and query multi-valued properties and meta-properties is in the Apache Tinkerpop documentation.

Graph options

You can set default graph options when initializing the cluster. They will be used for all graph statements. For example, to avoid repeating setGraphName("demo") on each statement:

DseCluster dseCluster = DseCluster.builder()
        .addContactPoint("127.0.0.1")
        .withGraphOptions(new GraphOptions().setGraphName("demo"))
        .build();

You can also retrieve and change the options at runtime (be careful about concurrency though, the changes will be visible across all client threads):

GraphOptions graphOptions = dseCluster.getConfiguration().getGraphOptions();
graphOptions.setGraphName("demo2");

If an option is set manually on a GraphStatement, it always takes precedence; otherwise the default option is used. This might be a problem if a default graph name is set, but you explicitly want to execute a statement targeting system, for which no graph name must be set. In that situation, use GraphStatement#setSystemQuery():

GraphStatement s = new SimpleGraphStatement("system.graph('demo').ifNotExists().create()")
        .setSystemQuery();
dseSession.executeGraph(s);

Timeouts

The default maximum time limit for executing a graph query is configured server side, in dse.yaml.

By default the Java driver will rely on that server-side configuration. This means that by default, after sending a request, the driver will wait until the server responds with a result or an error message, or times out.

This can be changed if the client needs a lower timeout. A timeout for the client can be set either on the Cluster’s GraphOptions object and will apply to all Graph queries, or individually on each GraphStatement object, through the setReadTimeoutMillis() method. Note that the server will abort a query once the client has stopped waiting for it, so there’s no risk of leaving long-running queries on the server.

Query Parameters

Graph query parameters are always named. Parameter bindings are passed as a Map<String, Object> alongside the query (Guava’s ImmutableMap provides a convenient way to build maps on the fly):

import com.google.common.collect.ImmutableMap;

// One-liner:
dseSession.executeGraph("g.addV(label, vertexLabel)",
        ImmutableMap.<String, Object>of("vertexLabel", "test_vertex_2"));

// Alternative syntax:
dseSession.executeGraph("g.addV(label, vertexLabel)",
        ImmutableMap.<String, Object>builder()
                .put("vertexLabel", "test_vertex_2")
                .build());

Another way to specify parameters is to use the set() call on a SimpleGraphStatement:

SimpleGraphStatement s = new SimpleGraphStatement("g.addV(label, vertexLabel)")
        .set("vertexLabel", "test_vertex_2");
dseSession.executeGraph(s);

Note that, unlike in CQL, Gremlin placeholders are not prefixed with “:”.

The classes supported for the parameters are listed in the Data types compatibility matrix.

In addition, you can inject:

  • any GraphNode instance. In particular, the identifier of a previously retrieved vertex or edge:

    Vertex v1 = dseSession.executeGraph("g.V().hasLabel('test_vertex')").one().asVertex();
    Vertex v2 = dseSession.executeGraph("g.V().hasLabel('test_vertex_2')").one().asVertex();
    
    SimpleGraphStatement s = new SimpleGraphStatement(
            "def v1 = g.V(id1).next()\n" +
                    "def v2 = g.V(id2).next()\n" +
                    "v1.addEdge('relates', v2)")
            .set("id1", v1.getId())
            .set("id2", v2.getId());
    
    dseSession.executeGraph(s);
    
  • any instance of Element. Its identifier will be serialized, so the following is equivalent to the last example above:

    Vertex v1 = dseSession.executeGraph("g.V().hasLabel('test_vertex')").one().asVertex();
    Vertex v2 = dseSession.executeGraph("g.V().hasLabel('test_vertex_2')").one().asVertex();
    
    SimpleGraphStatement s = new SimpleGraphStatement(
            "def v1 = g.V(id1).next()\n" +
                    "def v2 = g.V(id2).next()\n" +
                    "v1.addEdge('relates', v2)")
            .set("id1", v1)
            .set("id2", v2);
    
    dseSession.executeGraph(s);
    

Prepared statements

Prepared graph statements are not supported by DSE yet (they will be added in the near future).