To use the Graph API include the following jars in your classpath:
orient-commons-*.jar
orientdb-core-*.jar
blueprints-core-*.jar
blueprints-orient-graph-*.jar / orientdb-graphdb-*.jar (depending on version)
If you're connected to a remote server (not local/plocal/memory modes) include also:
orientdb-client-*.jar
orientdb-enterprise-*.jar
To also use the TinkerPop Pipes tool include also:
pipes-*.jar
To also use the TinkerPop Gremlin language include also:
gremlin-java-*.jar
gremlin-groovy-*.jar
groovy-*.jar
NOTE: If you use OrientDB v1.4.x or major with the Deprecated Native API, set this configuration to make the new graphs compatible with the deprecated API:
alter database custom useLightweightEdges=false
alter database custom useClassForEdgeLabel=false
alter database custom useClassForVertexLabel=false
alter database custom useVertexFieldsForEdgeLabels=false
Tinkerpop is a complete stack of projects to handle Graphs:
OrientDB supports different kind of storages and depends by the Database URL used:
Before to work with a graph you need an instance of OrientGraph class. The constructor gets a URL that is the location of database. If the database already exists, it will be opened, otherwise will be created. In multi-threads applications use one OrientGraph instance per thread.
Remember always to close the graph once done using the .shutdown()
method.
Example:
OrientGraph graph = new OrientGraph("plocal:C:/temp/graph/db");
try {
...
} finally {
graph.shutdown();
}
Starting from v1.7 the best way to get a Graph instance is through the OrientGraphFactory. To know more: Use the Graph Factory. Example:
// AT THE BEGINNING
OrientGraphFactory factory = new OrientGraphFactory("plocal:C:/temp/graph/db").setupPool(1,10);
// EVERY TIME YOU NEED A GRAPH INSTANCE
OrientGraph graph = factory.getTx();
try {
...
} finally {
graph.shutdown();
}
Every time the graph is modified an implicit transaction is started automatically if no previous transaction was running. Transactions are committed automatically when the graph is closed by calling the shutdown() method or by explicit commit(). To rollback changes call the rollback() method.
Changes inside a transaction will be temporary till the commit or the close of the graph instance. Concurrent threads or external clients can see the changes only when the transaction has been fully committed.
Full example:
try{
Vertex luca = graph.addVertex(null); // 1st OPERATION: IMPLICITLY BEGIN A TRANSACTION
luca.setProperty( "name", "Luca" );
Vertex marko = graph.addVertex(null);
marko.setProperty( "name", "Marko" );
Edge lucaKnowsMarko = graph.addEdge(null, luca, marko, "knows");
graph.commit();
} catch( Exception e ) {
graph.rollback();
}
Surrounding the transaction between a try/catch assure that any errors will rollback the transaction to the previous status for all the involved elements.
NOTE: To work against a graph use always transactional OrientGraph instances, an never non-transactional ones to avoid graph corruption on multi-thread changes.
To create a new Vertex in the current Graph call the Vertex OrientGraph.addVertex(Object id)) method. Note that the id parameter is ignored since OrientDB implementation assigns a unique-id once the vertex is created. To return it use Vertex.getId()). Example:
Vertex v = graph.addVertex(null);
System.out.println("Created vertex: " + v.getId());
An edge links two vertexes previously created. To create a new Edge in the current Graph call the Edge OrientGraph.addEdge(Object id, Vertex outVertex, Vertex inVertex, String label )) method. Note that the id parameter is ignored since OrientDB implementation assigns a unique-id once the edge is created. To return it use Edge.getId()). outVertex
is the vertex instance where the edge starts and inVertex
is the vertex instance where the edge ends. label
is the edge's label. Null to not assign it.
Example:
Vertex luca = graph.addVertex(null);
luca.setProperty("name", "Luca");
Vertex marko = graph.addVertex(null);
marko.setProperty("name", "Marko");
Edge lucaKnowsMarko = graph.addEdge(null, luca, marko, "knows");
System.out.println("Created edge: " + lucaKnowsMarko.getId());
To retrieve all the vertices use the getVertices()
method:
for (Vertex v : graph.getVertices()) {
System.out.println(v.getProperty("name"));
}
To retrieve all the vertices use the getEdges()) method:
for (Edge e : graph.getEdges()) {
System.out.println(e.getProperty("age"));
}
NOTE: Starting from OrientDB v1.4.x edges by default are stored as links not as records (i.e. useLightweightEdges=true by default). This is to improve performance. As a consequence, getEdges will only retrieve records of class E. With useLightweightEdges=true, records of class E are only created under certain circumstances (e.g. if the Edge has properties) otherwise they will be links on the in and out vertices. If you really want getEdges to return all edges, disable the Lightweight-Edge feature by executing this command once: alter database custom useLightweightEdges=false
. This will only take effect for new edges so you'll have to convert the links to actual edges before getEdges will return all edges. For more information look at: https://github.com/orientechnologies/orientdb/wiki/Troubleshooting#why-i-cant-see-all-the-edges.
To remove a vertex from the current Graph call the OrientGraph.removeVertex(Vertex vertex)) method. The vertex will be disconnected from the graph and then removed. Disconnection means that all the vertex's edges will be deleted as well. Example:
graph.removeVertex(luca);
To remove an edge from the current Graph call the OrientGraph.removeEdge(Edge edge)) method. The edge will be removed and the two vertexes will result not connected anymore. Example:
graph.removeEdge(lucaKnowsMarko);
Vertexes and Edges can have multiple properties where the key is a String and the value can be any supported OrientDB types.
Example:
vertex2.setProperty("x", 30.0f);
vertex2.setProperty("y", ((float) vertex1.getProperty( "y" )) / 2);
for (String property : vertex2.getPropertyKeys()) {
System.out.println("Property: " + property + "=" + vertex2.getProperty(property));
}
vertex1.removeProperty("y");
Blueprints extension OrientDB Blueprints implementation supports setting of multiple properties in one shot against Vertices and Edges. This improves performance avoiding to save the graph element at every property set: setProperties(Object ...)). Example:
vertex.setProperties( "name", "Jill", "age", 33, "city", "Rome", "born", "Victoria, TX" );
You can also pass a Map of values as first argument. In this case all the map entries will be set as element properties:
Map<String,Object> props = new HashMap<String,Object>();
props.put("name", "Jill");
props.put("age", 33);
props.put("city", "Rome");
props.put("born", "Victoria, TX");
vertex.setProperties(props);
If you want to create a vertex or an edge setting also the initial properties, OrientDB Blueprints implementation offers new methods to do it:
graph.addVertex( "class:Customer", "name", "Jill", "age", 33, "city", "Rome", "born", "Victoria, TX" );
This creates a new Vertex of class Customer
with the properties: name
, age
, city
, and born
. The same is for Edges:
person1.addEdge("class:Friend", person2, null, null, "since", "2013-07-30");
This creates a new Edge of class Friend
between vertices person1
and person2
with the property since
.
Both methods accepts a Map<String, Object>
as parameter to set one property per map entry (see above for the example).
These methods are specially useful if you've declared constraints in the schema, for example a property cannot be null, and only using these methods will the validation checks succeed.
OrientDB allows execution queries against any field of vertices and edges, indexed and not-indexed. The first rule to speed up queries is to setup indexes on the key properties you use in query. For example if you have a query that is looking for all the vertices with the name 'OrientDB' you do:
graph.getVertices("name", "OrientDB");
Without an index against the property "name" this execution could take a lot of time. So let's create a new index against the "name" property:
graph.createKeyIndex("name", Vertex.class);
If the name MUST be unique you can enforce this constraint by setting the index as "UNIQUE" (this is an OrientDB only feature):
graph.createKeyIndex("name", Vertex.class, new Parameter("type", "UNIQUE"));
This constraint will be applied to all the Vertex and sub-types instances. To specify an index against a custom type like the "Customer" vertices use the additional parameter "class":
graph.createKeyIndex("name", Vertex.class, new Parameter("class", "Customer"));
You can also have both UNIQUE index against custom types:
graph.createKeyIndex("name", Vertex.class, new Parameter("type", "UNIQUE"), new Parameter("class", "Customer"));
To get a vertex or an edge by key prefix the class name to the field. For the example above use Customer.name
in place of only name
to use the index created against the field name
of class Customer
:
for (Vertex v : graph.getVertices("Customer.name", "Jay")) {
System.out.println("Found vertex: " + v);
}
If the class name is not passed, then "V" is taken for vertices and "E" for edges:
graph.getVertices("name", "Jay");
graph.getEdges("age", 20);
For more information about indexes look at Index guide.
To speed up operation like on massive insertion you could avoid transactions at all by using a different class then OrientGraph: OrientGraphNoTx. In this case each operation is atomic and data is updated at each operation. When the method returns the underlying storage is updated. Use this for bulk insert and massive operations.
NOTE: The usage of Non-Transactional graphs could drive corruption of the graph on multi-thread changes, so use Non-Transactional graph instances only for non multi-threads operations.
Starting from v1.6 OrientDB supports configuration of the graph by setting all the properties on construction:
Name | Description | Default value |
---|---|---|
blueprints.orientdb.url | Database URL | - |
blueprints.orientdb.username | User name | admin |
blueprints.orientdb.password | User password | admin |
blueprints.orientdb.saveOriginalIds | Saves the original element IDs by using the property _id. This could be useful on import of graph to preserve original ids | false |
blueprints.orientdb.keepInMemoryReferences | Avoid to keep records in memory but only RIDs | false |
blueprints.orientdb.useCustomClassesForEdges | Use Edge's label as OrientDB class. If doesn't exist create it under the hood | true |
blueprints.orientdb.useCustomClassesForVertex | Use Vertex's label as OrientDB class. If doesn't exist create it under the hood | true |
blueprints.orientdb.useVertexFieldsForEdgeLabels | Store the edge relationships in vertex by using the Edge's class. This allow to use multiple fields and make faster traversal by edge's label (class) | true |
blueprints.orientdb.lightweightEdges | Uses lightweight edges. This avoid to create a physical document per edge. Documents are created only when they have properties | true |
blueprints.orientdb.autoStartTx | Auto start a transaction as soon the graph is changed by adding/remote vertices and edges and properties | true |
If you use GREMLIN language with OrientDB remember to initialize it with:
OGremlinHelper.global().create()
Look at these pages about GREMLIN usage:
Multi-threads application must use one OrientGraph instance per thread. For more information about multi-threading look at Java Multi Threading.
OrientDB is a Graph Database with steroids because it merges the graph, document and object-oriented worlds together. Below are some of the features exclusive to OrientDB.
OrientDB supports custom types for vertices and edges in an Object Oriented manner. Even if this isn't supported directly by Blueprints there are some tricks to use them. Look at the Graph Schema page to know how to create a schema and work against types.
OrientDB added few variants to the Blueprints methods to work with types.
To retrieve all the vertices of Person
class use the special getVerticesOfClass(String className)
method:
for (Vertex v : graph.getVerticesOfClass("Person")) {
System.out.println(v.getProperty("name"));
}
All the vertices of class Person and all subclasses will be retrieved. This is because by default the polymorphism is used. If you're interested ONLY into Person
vertices excluding any sub-types use the getVerticesOfClass(String className, boolean polymorphic)
method specifying false
in the second argument polymorphic
:
for (Vertex v : graph.getVerticesOfClass("Person", false)) {
System.out.println(v.getProperty("name"));
}
The same variants apply also to the getEdges()
method as:
getEdgesOfClass(String className)
andgetEdgesOfClass(String className, boolean polymorphic)
OrientDB, by default, uses Set to handle the edge collection. Sometimes it's better having an ordered List to access to the edge by offset. Example:
person.createEdgeProperty(Direction.OUT, "Photos").setOrdered(true);
Every time you access to the edge collection the edges are ordered. Below an example to print all the photos in ordered way.
for (Edge e : loadedPerson.getEdges(Direction.OUT, "Photos")) {
System.out.println( "Photo name: " + e.getVertex(Direction.IN).getProperty("name") );
}
To access to the underlying edge list you've to use the Document Database API. Example to swap 10th photo with last one.
// REPLACE EDGE Photos
List<ODocument> photos = loadedPerson.getRecord().field("out_Photos");
photos.add(photos.remove(9));
When you work with Web Applications, it’s very common to query elements and render them to the user to let him to apply some changes. Once the user updates some fields and press the “save” button, what happens?
Before now the developer had to track the changes in a separate structure, load the vertex/edge from the database and apply the changes to the element.
Starting from OrientDB v1.7 we added 2 new methods to the Graph API against OrientElement and OrientBaseGraph classes:
OrientElement.detach()
OrientElement.attach()
OrientBaseGraph.detach(OrientElement)
OrientBaseGraph.attach(OrientElement)
Detach methods fetch all the record content in RAM and reset the connection to the Graph instance. This allow to modify the element off-line and re-attach it once finished.
Once the detached element has been modified, to be saved back to the database you need to call the attach()
method. It restore back the connection between the Graph Element and the Graph Instance.
The first step is load some vertex and detach them.
OrientGraph g = OrientGraph("plocal:/temp/db");
try {
Iterable<OrientVertex> results = g.query().has("name", EQUALS, "fast");
for (OrientVertex v : results)
v.detach();
} finally {
g.shutdown();
}
After a while the element is updated (from GUI or by application)
v.setProperty("name", "super fast!");
On “save” button re-attach the element and save it to the database.
OrientGraph g = OrientGraph("plocal:/temp/db");
try {
v.attach(g);
v.save();
} finally {
g.shutdown();
}
Does detach go recursively to detach all connected elements? No, it works only at the current element level.
Can I add edge against detached elements? No, you can only get/set/remove property while is detached. Any other operation that requires the database will throw an IllegalStateException.
OrientDB supports optimistic transactions, so no lock is kept when a transaction is running, but at commit time each graph element version is checked to see if have been updated by other clients. This is the reason why you should write your code to be concurrency-proof by handling the concurrent updating case:
for (int retry = 0; retry < maxRetries; ++retry) {
try {
// LOOKUP FOR THE INVOICE VERTEX
Vertex invoice = graph.getVertices("invoiceId", 2323);
// CREATE A NEW ITEM
Vertex invoiceItem = graph.addVertex("class:InvoiceItem");
invoiceItem.field("price", 1000);
// ADD IT TO THE INVOICE
invoice.addEdge(invoiceItem);
graph.commit();
break;
} catch( OTransactionException e ) {
// SOMEONE HAVE UPDATE THE INVOICE VERTEX AT THE SAME TIME, RETRY IT
}
}
Starting from v.1.5 transactions auto retry if a timeout exception occurs. This happens in case of deadlocks or network latency. By default the AutoRetry setting is 10 but you can adjust, or disable with 0, by calling:
((OTransactionOptimistic) graph.getRawGraph().getTransaction()).setAutoRetries( 0 );
OrientDB Blueprints implementation allows to execute commands using SQL, Javascript and all the other supported languages.
for (Vertex v : (Iterable<Vertex>) graph.command(
new OCommandSQL("select expand( out('bough') ) from Customer where name = 'Jay'")).execute()) {
System.out.println("- Bought: " + v);
}
As log as queries you can execute any SQL command like CREATE VERTEX
, UPDATE
and DELETE VERTEX
. In the example below it sets a new property called "local" to true to all the Customers lives in Rome:
int modified = graph.command(
new OCommandSQL("UPDATE Customer SET local = true WHERE 'Rome' IN out('lives').name")).execute());
To execute asynchronous query:
graph.command(
new OSQLAsynchQuery<Vertex>("select from Member",
new OCommandResultListener() {
int resultCount =0;
@Override
public boolean result(Object iRecord) {
resultCount++;
Vertex doc = graph.getVertex( iRecord );
return resultCount < 100;
}
} ).execute();
To execute multiple SQL commands in a batch, use the OCommandScript using SQL as the language. This is recommended in case of creating edges on the server side, minimizing the network roundtrip:
String cmd = "begin\n";
cmd += "let a = create vertex set script = true\n";
cmd += "let b = select from v limit 1\n";
cmd += "let e = create edge from $a to $b retry 100\n";
cmd += "commit\n";
cmd += "return $e";
OIdentifiable edge = graph.command(new OCommandScript("sql", cmd)).execute();
For more information look at SQL Batch.
To execute a database function, written in Javascript or any other supported languages. In the example below we imagine to have written the function updateAllTheCustomersInCity(cityName)
that execute the same update like above. Note the 'Rome' attribute passed in execute()
method:
graph.command(
new OCommandFunction("updateAllTheCustomersInCity")).execute("Rome"));
To execute code at the server side you can select between the supported language (by default Javascript):
graph.command(
new OCommandScript("javascript", "for(var i=0;i<10;++i){ print('\nHello World!'); }")).execute());
This prints 10 times the line "Hello World!" in the server console, or in the local console if the database has been open in "plocal" mode.
Since TinkerPop Blueprints API is quite raw and doesn't provide ad-hoc methods for very common use cases you could need to access to the underlying ODatabaseGraphTx object to better use the graph-engine under the hood. Commons operations are:
The OrientGraph class provides the method .getRawGraph()
to return the underlying database: [Document Database].
Example:
final OrientGraph graph = new OrientGraph("plocal:C:/temp/graph/db");
try {
List<ODocument> result = graph.getRawGraph().query(
new OSQLSynchQuery("select from V where color = 'red'"));
} finally {
graph.shutdown();
}
If you want to use the OrientDB security, use the constructor that retrieves the URL, user and password. To know more about OrientDB security visit Security. By default the "admin" user is used.
Look at the Performance Tuning Blueprints page.