Graph databases are a niche in the database world. They are not nearly as widely used as relational database, NoSQL - Key Value Store and NoSQL Document Stores.
But in recent years they have seen some usage in the advanced data analytics world due to their ability to map complex relations.
So maybe worth taking a look at.
A graph database are build on two concepts:
Illustration:
Graph databases started to show up in the 00's.
But graph databases are really an evolution of the network database model from the 60's and 70's. They share the conecept of linked data items. The difference is that the modern graph databases comes with much more powerful API's for navigating the links.
A graph database is typical build on top of a more primitive storage engine: relational database, NoSQL - Key Value Store or NoSQL Document Stores.
Well known graph databases are:
We will use a very simple data set for examples:
Neo4j is an open source graph database first released in 2007.
It is one of the most well known graph databases.
It is available as open source under GPL/AGPL, under commercial license and as SaaS cloud (AuraDB).
Supported platforms | Any platform with Java |
Supported languages | Java and other JVM languages, C#, VB.NET, Python, PHP, JavaScript m.fl. |
Features | |
Missing features |
It is accessible via both a binary protocol (BOLT) and HTTP.
It supports both the Cypher and the Gremlin query language.
It is a very powerful graph database when one master the Cypher language. And the API's are very consistent.
Disclaimer: I have never used Neo4j for anything before writing this, so some of the code may very well be less than optimal.
Example:
The Java driver is a separate product not include with the server. It can be downloaded from Maven repo as neo4j-java-driver.
package nosql.neo4j.graph;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.Record;
import org.neo4j.driver.Session;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Path;
public class TestG {
private static String nstring(Node n) {
return String.format("%d (%s)", n.asMap().get("id"), n.asMap().get("text"));
}
private static void showConnects(Session db, int id) {
Node n = db.run(String.format("MATCH (n:V) WHERE n.id = %d RETURN n", id)).next().get("n").asNode();
System.out.printf("Node %s is connected to:\n", nstring(n));
for(Record r : db.run(String.format("MATCH (V { id: %d })--(n) RETURN n", id)).list()) {
Node nc = r.get("n").asNode();
System.out.printf(" Node %s\n", nstring(nc));
}
}
private static void walk(Session db, int id) {
Node n = db.run(String.format("MATCH (n:V) WHERE n.id = %d RETURN n", id)).next().get("n").asNode();
System.out.print("Walk: ");
System.out.print(nstring(n));
Node lastnc = null;
for(Record r : db.run(String.format("MATCH (V { id: %d })-[:E*1..10]->(n) RETURN n", id)).list()) {
Node nc = r.get("n").asNode();
if(nc.equals(lastnc)) continue; // this seems necessary to avoid an occasionally repeat - I don't know why
lastnc = nc;
System.out.print(" -> ");
System.out.print(nstring(nc));
}
System.out.println();
}
private static void findWay(Session db, int idfrom, int idto) {
System.out.print("Find: ");
Path p = db.run(String.format("MATCH (n1:V { id: %d }),(n2:V { id: %d }), p = shortestPath((n1)-[*1..10]->(n2)) RETURN p", idfrom, idto)).next().get("p").asPath();
boolean first = true;
for(Node n : p.nodes()) {
if(first) {
first = false;
} else {
System.out.print(" -> ");
}
System.out.print(nstring(n));
}
System.out.println();
}
public static void main(String[] args) {
// open
Driver cli = GraphDatabase.driver("bolt://192.168.60.129:7687", AuthTokens.basic("neo4j", "hemmeligt"));
Session db = cli.session();
// put
db.run("CREATE (n:V { id:1, text:'This is 1'})");
db.run("CREATE (n:V { id:2, text:'This is 2'})");
db.run("CREATE (n:V { id:3, text:'This is 3'})");
db.run("CREATE (n:V { id:4, text:'This is 4'})");
db.run("CREATE (n:V { id:5, text:'This is 5'})");
db.run("CREATE (n:V { id:6, text:'This is 6'})");
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=1 AND n2.id=2 CREATE (n1)-[e:E]->(n2)");
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=2 AND n2.id=3 CREATE (n1)-[e:E]->(n2)");
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=2 AND n2.id=4 CREATE (n1)-[e:E]->(n2)");
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=3 AND n2.id=5 CREATE (n1)-[e:E]->(n2)");
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=3 AND n2.id=6 CREATE (n1)-[e:E]->(n2)");
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=5 AND n2.id=6 CREATE (n1)-[e:E]->(n2)");
// query connections
showConnects(db, 1);
showConnects(db, 2);
showConnects(db, 3);
showConnects(db, 4);
showConnects(db, 5);
showConnects(db, 6);
// traverse
walk(db, 1);
walk(db, 2);
walk(db, 3);
walk(db, 4);
walk(db, 5);
walk(db, 6);
// shortest path
findWay(db, 1, 2);
findWay(db, 1, 3);
findWay(db, 1, 4);
findWay(db, 1, 5);
findWay(db, 1, 6);
// close
db.close();
cli.close();
}
}
The .NET driver can be retrived via NuGet of Neo4j.Driver.Simple.
using System;
using Neo4j.Driver;
namespace NoSQL.Neo4j.Graph
{
public class Program
{
private static String Nstring(INode n)
{
return string.Format("{0} ({1})", n.Properties["id"], n.Properties["text"]);
}
private static void ShowConnects(ISession db, int id)
{
INode n = db.Run(string.Format("MATCH (n:V) WHERE n.id = {0} RETURN n", id)).Peek()["n"].As<INode>();
Console.WriteLine("Node {0} is connected to:", Nstring(n));
foreach(IRecord r in db.Run(string.Format("MATCH (V {{ id: {0} }})--(n) RETURN n", id)))
{
INode nc = r["n"].As<INode>();
Console.WriteLine(" Node {0}", Nstring(nc));
}
}
private static void Walk(ISession db, int id)
{
INode n = db.Run(string.Format("MATCH (n:V) WHERE n.id = {0} RETURN n", id)).Peek()["n"].As<INode>();
Console.Write("Walk: ");
Console.Write(Nstring(n));
INode lastnc = null;
foreach(IRecord r in db.Run(string.Format("MATCH (V {{ id:{0} }})-[:E*1..10]->(n) RETURN n", id)))
{
INode nc = r["n"].As<INode>();
if(nc.Equals(lastnc)) continue; // this seems necessary to avoid an occasionally repeat - I don't know why
lastnc = nc;
Console.Write(" -> ");
Console.Write(Nstring(nc));
}
Console.WriteLine();
}
private static void FindWay(ISession db, int idfrom, int idto)
{
Console.Write("Find: ");
IPath p = db.Run(string.Format("MATCH (n1:V {{ id: {0} }}),(n2:V {{ id: {1} }}), p = shortestPath((n1)-[*1..10]->(n2)) RETURN p", idfrom, idto)).Peek()["p"].As<IPath>();
bool first = true;
foreach(INode n in p.Nodes)
{
if(first)
{
first = false;
}
else
{
Console.Write(" -> ");
}
Console.Write(Nstring(n));
}
Console.WriteLine();
}
public static void Main(string[] args)
{
// open
IDriver cli = GraphDatabase.Driver("bolt://192.168.60.129:7687", AuthTokens.Basic("neo4j", "hemmeligt"));
ISession db = cli.Session();
// put
db.Run("CREATE (n:V { id:1, text:'This is 1'})");
db.Run("CREATE (n:V { id:2, text:'This is 2'})");
db.Run("CREATE (n:V { id:3, text:'This is 3'})");
db.Run("CREATE (n:V { id:4, text:'This is 4'})");
db.Run("CREATE (n:V { id:5, text:'This is 5'})");
db.Run("CREATE (n:V { id:6, text:'This is 6'})");
db.Run("MATCH (n1:V),(n2:V) WHERE n1.id=1 AND n2.id=2 CREATE (n1)-[e:E]->(n2)");
db.Run("MATCH (n1:V),(n2:V) WHERE n1.id=2 AND n2.id=3 CREATE (n1)-[e:E]->(n2)");
db.Run("MATCH (n1:V),(n2:V) WHERE n1.id=2 AND n2.id=4 CREATE (n1)-[e:E]->(n2)");
db.Run("MATCH (n1:V),(n2:V) WHERE n1.id=3 AND n2.id=5 CREATE (n1)-[e:E]->(n2)");
db.Run("MATCH (n1:V),(n2:V) WHERE n1.id=3 AND n2.id=6 CREATE (n1)-[e:E]->(n2)");
db.Run("MATCH (n1:V),(n2:V) WHERE n1.id=5 AND n2.id=6 CREATE (n1)-[e:E]->(n2)");
// query connections
ShowConnects(db, 1);
ShowConnects(db, 2);
ShowConnects(db, 3);
ShowConnects(db, 4);
ShowConnects(db, 5);
ShowConnects(db, 6);
// traverse
Walk(db, 1);
Walk(db, 2);
Walk(db, 3);
Walk(db, 4);
Walk(db, 5);
Walk(db, 6);
// shortest path
FindWay(db, 1, 2);
FindWay(db, 1, 3);
FindWay(db, 1, 4);
FindWay(db, 1, 5);
FindWay(db, 1, 6);
// close
db.Dispose();
cli.Dispose();
}
}
}
The Python driver can be installed as:
pip install neo4j
from neo4j import GraphDatabase
def nstring(n):
return '%d (%s)' % (n['id'],n['text'])
def show_connects(db, id):
n = db.run('MATCH (n:V) WHERE n.id = %d RETURN n' % (id)).single()['n']
print('Node %s is connected to:' % nstring(n))
for r in db.run('MATCH (V { id: %d })--(n) RETURN n' % (id)):
nc = r['n']
print(' Node %s' % (nstring(nc)))
def walk(db, id):
n = db.run('MATCH (n:V) WHERE n.id = %d RETURN n' % (id)).single()['n']
res = 'Walk: '
res = res + nstring(n)
lastnc = None
for r in db.run('MATCH (V { id: %d })-[:E*1..10]->(n) RETURN n' % (id)):
nc = r['n']
if nc == lastnc:
continue # this seems necessary to avoid an occasionally repeat - I don't know why
lastnc = nc
res = res + ' -> '
res = res + nstring(nc)
print(res)
def find_way(db, idfrom, idto):
res = 'Find: '
p = db.run('MATCH (n1:V { id: %d }),(n2:V { id: %d }), p = shortestPath((n1)-[*1..10]->(n2)) RETURN p' % (idfrom,idto)).single()['p']
first = True
for n in p.nodes:
if first:
first = False
else:
res = res + ' -> '
res = res + nstring(n)
print(res)
# open
cli = GraphDatabase.driver('bolt://192.168.60.129:7687', auth=('neo4j', 'hemmeligt'))
db = cli.session()
# put
db.run("CREATE (n:V { id:1, text:'This is 1'})")
db.run("CREATE (n:V { id:2, text:'This is 2'})")
db.run("CREATE (n:V { id:3, text:'This is 3'})")
db.run("CREATE (n:V { id:4, text:'This is 4'})")
db.run("CREATE (n:V { id:5, text:'This is 5'})")
db.run("CREATE (n:V { id:6, text:'This is 6'})")
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=1 AND n2.id=2 CREATE (n1)-[e:E]->(n2)")
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=2 AND n2.id=3 CREATE (n1)-[e:E]->(n2)")
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=2 AND n2.id=4 CREATE (n1)-[e:E]->(n2)")
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=3 AND n2.id=5 CREATE (n1)-[e:E]->(n2)")
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=3 AND n2.id=6 CREATE (n1)-[e:E]->(n2)")
db.run("MATCH (n1:V),(n2:V) WHERE n1.id=5 AND n2.id=6 CREATE (n1)-[e:E]->(n2)")
# query connections
show_connects(db, 1)
show_connects(db, 2)
show_connects(db, 3)
show_connects(db, 4)
show_connects(db, 5)
show_connects(db, 6)
# traverse
walk(db, 1)
walk(db, 2)
walk(db, 3)
walk(db, 4)
walk(db, 5)
walk(db, 6)
# shortest path
find_way(db, 1, 2)
find_way(db, 1, 3)
find_way(db, 1, 4)
find_way(db, 1, 5)
find_way(db, 1, 6)
#close
db.close()
cli.close()
The PHP driver can be installed as:
php composer.phar require "ostico/phporient"
<?php
require "vendor/autoload.php";
function nstring($n) {
return sprintf('%d (%s)', $n->getProperty('id'), $n->getProperty('text'));
}
function show_connects($db, $id) {
$n = $db->run(sprintf('MATCH (n:V) WHERE n.id = %d RETURN n', $id))[0]['n'];
echo sprintf("Node %s is connected to:\r\n", nstring($n));
foreach($db->run(sprintf('MATCH (V { id: %d })--(n) RETURN n', $id)) as $r) {
$nc = $r['n'];
echo sprintf(" Node %s\r\n", nstring($nc));
}
}
function walk($db, $id) {
$n = $db->run(sprintf('MATCH (n:V) WHERE n.id = %d RETURN n', $id))[0]['n'];
echo 'Walk: ';
echo nstring($n);
$lastnc = null;
foreach($db->run(sprintf('MATCH (V { id: %d })-[:E*1..10]->(n) RETURN n', $id)) as $r) {
$nc = $r['n'];
if($nc == $lastnc) continue; // this seems necessary to avoid an occasionally repeat - I don't know why
$lastnc = $nc;
echo ' -> ';
echo nstring($nc);
}
echo "\r\n";
}
function find_way($db, $idfrom, $idto) {
echo 'Find: ';
$p = $db->run(sprintf('MATCH (n1:V { id: %d }),(n2:V { id: %d }), p = shortestPath((n1)-[*1..10]->(n2)) RETURN p', $idfrom, $idto))[0]['p'];
$first = true;
foreach($p->getNodes() as $n) {
if($first) {
$first = false;
} else {
echo ' -> ';
}
echo nstring($n);
}
echo "\r\n";
}
use Laudis\Neo4j\ClientBuilder;
// open
$db = ClientBuilder::create()->withDriver('default', 'bolt://neo4j:hemmeligt@192.168.60.129')->build();
$db->run("MATCH (n:V) DETACH DELETE n");
// put
$db->run("CREATE (n:V { id:1, text:'This is 1'})");
$db->run("CREATE (n:V { id:2, text:'This is 2'})");
$db->run("CREATE (n:V { id:3, text:'This is 3'})");
$db->run("CREATE (n:V { id:4, text:'This is 4'})");
$db->run("CREATE (n:V { id:5, text:'This is 5'})");
$db->run("CREATE (n:V { id:6, text:'This is 6'})");
$db->run("MATCH (n1:V),(n2:V) WHERE n1.id=1 AND n2.id=2 CREATE (n1)-[e:E]->(n2)");
$db->run("MATCH (n1:V),(n2:V) WHERE n1.id=2 AND n2.id=3 CREATE (n1)-[e:E]->(n2)");
$db->run("MATCH (n1:V),(n2:V) WHERE n1.id=2 AND n2.id=4 CREATE (n1)-[e:E]->(n2)");
$db->run("MATCH (n1:V),(n2:V) WHERE n1.id=3 AND n2.id=5 CREATE (n1)-[e:E]->(n2)");
$db->run("MATCH (n1:V),(n2:V) WHERE n1.id=3 AND n2.id=6 CREATE (n1)-[e:E]->(n2)");
$db->run("MATCH (n1:V),(n2:V) WHERE n1.id=5 AND n2.id=6 CREATE (n1)-[e:E]->(n2)");
// query connections
show_connects($db, 1);
show_connects($db, 2);
show_connects($db, 3);
show_connects($db, 4);
show_connects($db, 5);
show_connects($db, 6);
# traverse
walk($db, 1);
walk($db, 2);
walk($db, 3);
walk($db, 4);
walk($db, 5);
walk($db, 6);
# shortest path
find_way($db, 1, 2);
find_way($db, 1, 3);
find_way($db, 1, 4);
find_way($db, 1, 5);
find_way($db, 1, 6);
?>
OrientDB is an open source document store and graph database first released in 2010.
The OrientDB company was acquired by CallidusCloud in 2017. And CallidusCloud was acquired by SAP in 2018. In 2021 the original developer forked it asArcadeDB.
Supported platforms | Any platform with Java |
Supported languages | Java and other JVM languages, C#, VB.NET, Python, PHP, JavaScript, C m.fl. |
Features | Both a NoSQL Document Store + Object Repository and a Graph Database Transaction support |
Missing features |
OrientDB comes with many different API's:
Disclaimer: I have never used OrientDB for anything before writing this, so some of the code may very well be less than optimal.
For examples of using OrientDB as document store see here.
Example:
Java API comes with OrientDB.
package nosql.orientdb.graph;
import com.orientechnologies.orient.core.db.ODatabaseSession;
import com.orientechnologies.orient.core.db.OrientDB;
import com.orientechnologies.orient.core.record.ODirection;
import com.orientechnologies.orient.core.record.OEdge;
import com.orientechnologies.orient.core.record.OVertex;
import com.orientechnologies.orient.core.sql.executor.OResultSet;
import com.orientechnologies.orient.core.sql.executor.OResult;
import com.orientechnologies.orient.core.id.ORID;
public class TestG {
private static String vstring(OVertex v) {
return String.format("%d (%s)", v.getProperty("id"), v.getProperty("text"));
}
private static void showConnects(OVertex v) {
System.out.printf("Node %s is connected to:\n", vstring(v));
for(OVertex vc : v.getVertices(ODirection.BOTH)) {
System.out.printf(" Node %s\n", vstring(vc));
}
}
private static void showConnects(ODatabaseSession db, int id) {
OVertex v = (OVertex)db.query("SELECT FROM V WHERE id = ?", id).next().getRecord().get();
showConnects(v);
}
private static void walk(ODatabaseSession db, OVertex v) {
OResultSet rs = db.query("TRAVERSE out() FROM " + v.getIdentity());
System.out.print("Walk: ");
boolean first = true;
while(rs.hasNext()) {
if(first) {
first = false;
} else {
System.out.print(" -> ");
}
OVertex vc = (OVertex)rs.next().getRecord().get();
System.out.print(vstring(vc));
}
System.out.println();
rs.close();
}
private static void walk(ODatabaseSession db, int id) {
OVertex v = (OVertex)db.query("SELECT FROM V WHERE id = ?", id).next().getRecord().get();
walk(db, v);
}
private static void findWay(ODatabaseSession db, OVertex vfrom, OVertex vto) {
OResultSet rs = db.query("SELECT SHORTESTPATH(?,?) AS path UNWIND path", vfrom.getIdentity(), vto.getIdentity());
System.out.print("Find: ");
boolean first = true;
while(rs.hasNext()) {
OResult r = rs.next();
ORID z = r.getProperty("path");
OVertex vc = db.load(z);
if(first) {
first = false;
} else {
System.out.print(" -> ");
}
System.out.print(vstring(vc));
}
rs.close();
System.out.println();
}
public static void main(String[] args) {
// open
OrientDB client = new OrientDB("remote:localhost", null);
ODatabaseSession db = client.open("testg", "admin", "hemmeligt");
// put
OVertex v1 = db.newVertex("V");
v1.setProperty("id", 1);
v1.setProperty("text", "This is V1");
db.save(v1);
OVertex v2 = db.newVertex("V");
v2.setProperty("id", 2);
v2.setProperty("text", "This is V2");
db.save(v2);
OVertex v3 = db.newVertex("V");
v3.setProperty("id", 3);
v3.setProperty("text", "This is V3");
db.save(v3);
OVertex v4 = db.newVertex("V");
v4.setProperty("id", 4);
v4.setProperty("text", "This is V4");
db.save(v4);
OVertex v5 = db.newVertex("V");
v5.setProperty("id", 5);
v5.setProperty("text", "This is V5");
db.save(v5);
OVertex v6 = db.newVertex("V");
v6.setProperty("id", 6);
v6.setProperty("text", "This is V6");
db.save(v6);
OEdge e12 = db.newEdge(v1, v2);
db.save(e12);
OEdge e23 = db.newEdge(v2, v3);
db.save(e23);
OEdge e24 = db.newEdge(v2, v4);
db.save(e24);
OEdge e35 = db.newEdge(v3, v5);
db.save(e35);
OEdge e36 = db.newEdge(v3, v6);
db.save(e36);
OEdge e56 = db.newEdge(v5, v6);
db.save(e56);
// query connections
showConnects(v1);
showConnects(v2);
showConnects(v3);
showConnects(v4);
showConnects(v5);
showConnects(v6);
showConnects(db, 1);
showConnects(db, 2);
showConnects(db, 3);
showConnects(db, 4);
showConnects(db, 5);
showConnects(db, 6);
// traverse
walk(db, v1);
walk(db, v2);
walk(db, v3);
walk(db, v4);
walk(db, v5);
walk(db, v6);
walk(db, 1);
walk(db, 2);
walk(db, 3);
walk(db, 4);
walk(db, 5);
walk(db, 6);
// shortest path
findWay(db, v1, v2);
findWay(db, v1, v3);
findWay(db, v1, v4);
findWay(db, v1, v5);
findWay(db, v1, v6);
// close
db.close();
client.close();
}
}
Java API comes with OrientDB.
package nosql.orientdb.graph;
import com.orientechnologies.orient.core.db.ODatabaseSession;
import com.orientechnologies.orient.core.db.OrientDB;
import com.orientechnologies.orient.core.record.OEdge;
import com.orientechnologies.orient.core.record.OVertex;
import com.orientechnologies.orient.core.sql.executor.OResultSet;
import com.orientechnologies.orient.core.sql.executor.OResult;
import com.orientechnologies.orient.core.id.ORID;
public class TestGX {
private static String vstring(OVertex v) {
return String.format("%d (%s)", v.getProperty("id"), v.getProperty("text"));
}
private static void showConnects(ODatabaseSession db, OVertex v) {
System.out.printf("Node %s is connected to:\n", vstring(v));
OResultSet rs = db.query("SELECT EXPAND(BOTH('E')) FROM ?", v.getIdentity());
while(rs.hasNext()) {
OVertex vc = rs.next().getVertex().get();
System.out.printf(" Node %s\n", vstring(vc));
}
}
private static void showConnects(ODatabaseSession db, int id) {
OVertex v = (OVertex)db.query("SELECT FROM V WHERE id = ?", id).next().getRecord().get();
showConnects(db, v);
}
private static void walk(ODatabaseSession db, OVertex v) {
OResultSet rs = db.query("TRAVERSE out() FROM " + v.getIdentity());
System.out.print("Walk: ");
boolean first = true;
while(rs.hasNext()) {
if(first) {
first = false;
} else {
System.out.print(" -> ");
}
OVertex vc = (OVertex)rs.next().getRecord().get();
System.out.print(vstring(vc));
}
System.out.println();
rs.close();
}
private static void walk(ODatabaseSession db, int id) {
OVertex v = (OVertex)db.query("SELECT FROM V WHERE id = ?", id).next().getRecord().get();
walk(db, v);
}
private static void findWay(ODatabaseSession db, OVertex vfrom, OVertex vto) {
OResultSet rs = db.query("SELECT SHORTESTPATH(?,?) AS path UNWIND path", vfrom.getIdentity(), vto.getIdentity());
System.out.print("Find: ");
boolean first = true;
while(rs.hasNext()) {
OResult r = rs.next();
ORID z = r.getProperty("path");
OVertex vc = db.load(z);
if(first) {
first = false;
} else {
System.out.print(" -> ");
}
System.out.print(vstring(vc));
}
rs.close();
System.out.println();
}
public static void main(String[] args) {
// open
OrientDB client = new OrientDB("remote:localhost", null);
ODatabaseSession db = client.open("testg", "admin", "hemmeligt");
// put
OVertex v1 = db.command("CREATE VERTEX V SET id = ?, text = ?", 1, "This is V1").next().getVertex().get();
OVertex v2 = db.command("CREATE VERTEX V SET id = ?, text = ?", 2, "This is V2").next().getVertex().get();
OVertex v3 = db.command("CREATE VERTEX V SET id = ?, text = ?", 3, "This is V3").next().getVertex().get();
OVertex v4 = db.command("CREATE VERTEX V SET id = ?, text = ?", 4, "This is V4").next().getVertex().get();
OVertex v5 = db.command("CREATE VERTEX V SET id = ?, text = ?", 5, "This is V5").next().getVertex().get();
OVertex v6 = db.command("CREATE VERTEX V SET id = ?, text = ?", 6, "This is V6").next().getVertex().get();
@SuppressWarnings("unused")
OEdge e12 = db.command("CREATE EDGE E FROM ? TO ?", v1.getIdentity(), v2.getIdentity()).next().getEdge().get();
@SuppressWarnings("unused")
OEdge e23 = db.command("CREATE EDGE E FROM ? TO ?", v2.getIdentity(), v3.getIdentity()).next().getEdge().get();
@SuppressWarnings("unused")
OEdge e24 = db.command("CREATE EDGE E FROM ? TO ?", v2.getIdentity(), v4.getIdentity()).next().getEdge().get();
@SuppressWarnings("unused")
OEdge e35 = db.command("CREATE EDGE E FROM ? TO ?", v3.getIdentity(), v5.getIdentity()).next().getEdge().get();
@SuppressWarnings("unused")
OEdge e36 = db.command("CREATE EDGE E FROM ? TO ?", v3.getIdentity(), v6.getIdentity()).next().getEdge().get();
@SuppressWarnings("unused")
OEdge e56 = db.command("CREATE EDGE E FROM ? TO ?", v5.getIdentity(), v6.getIdentity()).next().getEdge().get();
// query connections
showConnects(db, v1);
showConnects(db, v2);
showConnects(db, v3);
showConnects(db, v4);
showConnects(db, v5);
showConnects(db, v6);
showConnects(db, 1);
showConnects(db, 2);
showConnects(db, 3);
showConnects(db, 4);
showConnects(db, 5);
showConnects(db, 6);
// traverse
walk(db, v1);
walk(db, v2);
walk(db, v3);
walk(db, v4);
walk(db, v5);
walk(db, v6);
walk(db, 1);
walk(db, 2);
walk(db, 3);
walk(db, 4);
walk(db, 5);
walk(db, 6);
// shortest path
findWay(db, v1, v2);
findWay(db, v1, v3);
findWay(db, v1, v4);
findWay(db, v1, v5);
findWay(db, v1, v6);
// close
db.close();
client.close();
}
}
Java API comes with OrientDB.
package nosql.orientdb.graph;
import com.orientechnologies.orient.core.sql.OCommandSQL;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientGraph;
public class TestG {
private static String vstring(Vertex v) {
return String.format("%d (%s)", v.getProperty("xid"), v.getProperty("text"));
}
private static void showConnects(Vertex v) {
System.out.printf("Node %s is connected to:\n", vstring(v));
for(Vertex vc : v.getVertices(Direction.BOTH)) {
System.out.printf(" Node %s\n", vstring(vc));
}
}
private static void showConnects(OrientGraph db, int xid) {
Vertex v = (Vertex)db.getVertices("xid", xid).iterator().next();
showConnects(v);
}
private static void walk(OrientGraph db, Vertex v) {
Iterable<Vertex> rs = db.command(new OCommandSQL(String.format("TRAVERSE OUT() FROM %s", v.getId()))).execute();
System.out.print("Walk: ");
boolean first = true;
for(Vertex vc : rs) {
if(first) {
first = false;
} else {
System.out.print(" -> ");
}
System.out.print(vstring(vc));
}
System.out.println();
}
private static void walk(OrientGraph db, int xid) {
Vertex v = (Vertex)db.getVertices("xid", xid).iterator().next();
walk(db, v);
}
private static void findWay(OrientGraph db, Vertex vfrom, Vertex vto) {
Iterable<Vertex> rs = db.command(new OCommandSQL(String.format("SELECT SHORTESTPATH(%s,%s) AS path UNWIND path", vfrom.getId(), vto.getId()))).execute();
System.out.print("Find: ");
boolean first = true;
for(Vertex vc : rs) {
Vertex vc2 = (Vertex)vc.getProperty("path");
if(first) {
first = false;
} else {
System.out.print(" -> ");
}
System.out.print(vstring(vc2));
}
System.out.println();
}
public static void main(String[] args) {
// open
OrientGraph db = new OrientGraph("remote:192.168.60.129/testg", "admin", "hemmeligt");
// put
Vertex v1 = db.addVertex("V");
v1.setProperty("xid", 1);
v1.setProperty("text", "This is V1");
db.commit();
Vertex v2 = db.addVertex("V");
v2.setProperty("xid", 2);
v2.setProperty("text", "This is V2");
db.commit();
Vertex v3 = db.addVertex("V");
v3.setProperty("xid", 3);
v3.setProperty("text", "This is V3");
db.commit();
Vertex v4 = db.addVertex("V");
v4.setProperty("xid", 4);
v4.setProperty("text", "This is V4");
db.commit();
Vertex v5 = db.addVertex("V");
v5.setProperty("xid", 5);
v5.setProperty("text", "This is V5");
db.commit();
Vertex v6 = db.addVertex("V");
v6.setProperty("xid", 6);
v6.setProperty("text", "This is V6");
db.commit();
@SuppressWarnings("unused")
Edge e12 = db.addEdge("class:E", v1, v2, "e12");
db.commit();
@SuppressWarnings("unused")
Edge e23 = db.addEdge("class:E", v2, v3, "e23");
db.commit();
@SuppressWarnings("unused")
Edge e24 = db.addEdge("class:E", v2, v4, "e24");
db.commit();
@SuppressWarnings("unused")
Edge e35 = db.addEdge("class:E", v3, v5, "e35");
db.commit();
@SuppressWarnings("unused")
Edge e36 = db.addEdge("class:E", v3, v6, "e36");
db.commit();
@SuppressWarnings("unused")
Edge e56 = db.addEdge("class:E", v5, v6, "e56");
db.commit();
// query connections
showConnects(v1);
showConnects(v2);
showConnects(v3);
showConnects(v4);
showConnects(v5);
showConnects(v6);
showConnects(db, 1);
showConnects(db, 2);
showConnects(db, 3);
showConnects(db, 4);
showConnects(db, 5);
showConnects(db, 6);
// traverse
walk(db, v1);
walk(db, v2);
walk(db, v3);
walk(db, v4);
walk(db, v5);
walk(db, v6);
walk(db, 1);
walk(db, 2);
walk(db, 3);
walk(db, 4);
walk(db, 5);
walk(db, 6);
// shortest path
findWay(db, v1, v2);
findWay(db, v1, v3);
findWay(db, v1, v4);
findWay(db, v1, v5);
findWay(db, v1, v6);
}
}
Java API comes with OrientDB.
package nosql.orientdb.graph;
import com.orientechnologies.orient.core.sql.OCommandSQL;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientGraph;
public class TestGX {
private static String vstring(Vertex v) {
return String.format("%d (%s)", v.getProperty("id"), v.getProperty("text"));
}
private static void showConnects(OrientGraph db, Vertex v) {
System.out.printf("Node %s is connected to:\n", vstring(v));
Iterable<Vertex> rs = db.command(new OCommandSQL(String.format("SELECT EXPAND(BOTH('E')) FROM %s", v.getId()))).execute();
for(Vertex vc : rs) {
System.out.printf(" Node %s\n", vstring(vc));
}
}
private static void showConnects(OrientGraph db, int id) {
Iterable<Vertex> rs = db.command(new OCommandSQL(String.format("SELECT FROM V WHERE id = %d", id))).execute();
Vertex v = rs.iterator().next();
showConnects(db, v);
}
private static void walk(OrientGraph db, Vertex v) {
Iterable<Vertex> rs = db.command(new OCommandSQL(String.format("TRAVERSE out() FROM %s", v.getId()))).execute();
System.out.print("Walk: ");
boolean first = true;
for(Vertex vc : rs) {
if(first) {
first = false;
} else {
System.out.print(" -> ");
}
System.out.print(vstring(vc));
}
System.out.println();
}
private static void walk(OrientGraph db, int id) {
Iterable<Vertex> rs = db.command(new OCommandSQL(String.format("SELECT FROM V WHERE id = %d", id))).execute();
Vertex v = rs.iterator().next();
walk(db, v);
}
private static void findWay(OrientGraph db, Vertex vfrom, Vertex vto) {
Iterable<Vertex> rs = db.command(new OCommandSQL(String.format("SELECT SHORTESTPATH(%s,%s) AS path UNWIND path", vfrom.getId(), vto.getId()))).execute();
System.out.print("Find: ");
boolean first = true;
for(Vertex v : rs) {
Vertex vc = v.getProperty("path");
if(first) {
first = false;
} else {
System.out.print(" -> ");
}
System.out.print(vstring(vc));
}
System.out.println();
}
public static void main(String[] args) {
// open
OrientGraph db = new OrientGraph("remote:192.168.60.129/testg", "admin", "hemmeligt");
// put
Vertex v1 = db.command(new OCommandSQL(String.format("CREATE VERTEX V SET id = %d, text = '%s'", 1, "This is V1"))).execute();
Vertex v2 = db.command(new OCommandSQL(String.format("CREATE VERTEX V SET id = %d, text = '%s'", 2, "This is V2"))).execute();
Vertex v3 = db.command(new OCommandSQL(String.format("CREATE VERTEX V SET id = %d, text = '%s'", 3, "This is V3"))).execute();
Vertex v4 = db.command(new OCommandSQL(String.format("CREATE VERTEX V SET id = %d, text = '%s'", 4, "This is V4"))).execute();
Vertex v5 = db.command(new OCommandSQL(String.format("CREATE VERTEX V SET id = %d, text = '%s'", 5, "This is V5"))).execute();
Vertex v6 = db.command(new OCommandSQL(String.format("CREATE VERTEX V SET id = %d, text = '%s'", 6, "This is V6"))).execute();
db.command(new OCommandSQL(String.format("CREATE EDGE E FROM %s TO %s", v1.getId(), v2.getId()))).execute();
db.command(new OCommandSQL(String.format("CREATE EDGE E FROM %s TO %s", v2.getId(), v3.getId()))).execute();
db.command(new OCommandSQL(String.format("CREATE EDGE E FROM %s TO %s", v2.getId(), v4.getId()))).execute();
db.command(new OCommandSQL(String.format("CREATE EDGE E FROM %s TO %s", v3.getId(), v5.getId()))).execute();
db.command(new OCommandSQL(String.format("CREATE EDGE E FROM %s TO %s", v3.getId(), v6.getId()))).execute();
db.command(new OCommandSQL(String.format("CREATE EDGE E FROM %s TO %s", v5.getId(), v6.getId()))).execute();
// query connections
showConnects(db, v1);
showConnects(db, v2);
showConnects(db, v3);
showConnects(db, v4);
showConnects(db, v5);
showConnects(db, v6);
showConnects(db, 1);
showConnects(db, 2);
showConnects(db, 3);
showConnects(db, 4);
showConnects(db, 5);
showConnects(db, 6);
// traverse
walk(db, v1);
walk(db, v2);
walk(db, v3);
walk(db, v4);
walk(db, v5);
walk(db, v6);
walk(db, 1);
walk(db, 2);
walk(db, 3);
walk(db, 4);
walk(db, 5);
walk(db, 6);
// shortest path
findWay(db, v1, v2);
findWay(db, v1, v3);
findWay(db, v1, v4);
findWay(db, v1, v5);
findWay(db, v1, v6);
}
}
There are two OrientDB drivers for .NET - the old OrientDB-Net.binary.Innov8tive (note that '8' is not a typo!) driver and the new OrientDB.Net.Core driver. Both are available via NuGet.
As the new driver is still considered experimental we will use the old driver.
Note that this driver seems to only work with 2.x server - not with 3.x server.
using System;
using System.Collections.Generic;
using System.Linq;
using Orient.Client;
namespace TestG
{
public class Program
{
public static string VString(ODocument v)
{
return string.Format("{0} ({1})", v.GetField<int>("id"), v.GetField<string>("text"));
}
public static void ShowConnects(ODatabase db, ODocument v)
{
Console.WriteLine("Node {0} is connected to:", VString(v));
foreach(ODocument vc in db.Query(string.Format("SELECT EXPAND(BOTH('E')) FROM {0}", v.ORID)))
{
Console.WriteLine(" Node {0}", VString(vc));
}
}
public static void ShowConnects(ODatabase db, int id)
{
ShowConnects(db, db.Query(string.Format("SELECT FROM V where id = '{0}'", id))[0]);
}
public static void Walk(ODatabase db, ODocument v)
{
Console.Write("Walk: ");
bool first = true;
foreach(ODocument vc in db.Query(string.Format("TRAVERSE OUT() FROM {0}", v.ORID)))
{
if(first) {
first = false;
} else {
Console.Write(" -> ");
}
Console.Write("{0}", VString(vc));
}
Console.WriteLine();
}
public static void Walk(ODatabase db, int id)
{
Walk(db, db.Query(string.Format("SELECT FROM V where id = '{0}'", id))[0]);
}
public static void FindWay(ODatabase db, ODocument vfrom, ODocument vto)
{
List<ODocument> rs = db.Query(string.Format("SELECT SHORTESTPATH({0},{1}) AS path UNWIND path", vfrom.ORID, vto.ORID));
Console.Write("Find: ");
bool first = true;
foreach(ODocument r in rs)
{
ORID z = r.GetField<ORID>("path");
ODocument vc = db.Load.ORID(z).Run();
if(first) {
first = false;
} else {
Console.Write(" -> ");
}
Console.Write("{0}", VString(vc));
}
Console.WriteLine();
}
public static void Main(string[] args)
{
// open
ODatabase db = new ODatabase("192.168.60.129", 2424, "testg", ODatabaseType.Graph, "admin", "hemmeligt");
// put
ODocument v1 = db.Command("CREATE VERTEX V SET id = 1, text = 'This is V1'").ToSingle();
ODocument v2 = db.Command("CREATE VERTEX V SET id = 2, text = 'This is V2'").ToSingle();
ODocument v3 = db.Command("CREATE VERTEX V SET id = 3, text = 'This is V3'").ToSingle();
ODocument v4 = db.Command("CREATE VERTEX V SET id = 4, text = 'This is V4'").ToSingle();
ODocument v5 = db.Command("CREATE VERTEX V SET id = 5, text = 'This is V5'").ToSingle();
ODocument v6 = db.Command("CREATE VERTEX V SET id = 6, text = 'This is V6'").ToSingle();
ODocument e12 = db.Command(String.Format("CREATE EDGE E FROM {0} TO {1}", v1.ORID, v2.ORID)).ToSingle();
ODocument e23 = db.Command(String.Format("CREATE EDGE E FROM {0} TO {1}", v2.ORID, v3.ORID)).ToSingle();
ODocument e24 = db.Command(String.Format("CREATE EDGE E FROM {0} TO {1}", v2.ORID, v4.ORID)).ToSingle();
ODocument e35 = db.Command(String.Format("CREATE EDGE E FROM {0} TO {1}", v3.ORID, v5.ORID)).ToSingle();
ODocument e36 = db.Command(String.Format("CREATE EDGE E FROM {0} TO {1}", v3.ORID, v6.ORID)).ToSingle();
ODocument e56 = db.Command(String.Format("CREATE EDGE E FROM {0} TO {1}", v5.ORID, v6.ORID)).ToSingle();
// query connections
ShowConnects(db, v1);
ShowConnects(db, v2);
ShowConnects(db, v3);
ShowConnects(db, v4);
ShowConnects(db, v5);
ShowConnects(db, v6);
ShowConnects(db, 1);
ShowConnects(db, 2);
ShowConnects(db, 3);
ShowConnects(db, 4);
ShowConnects(db, 5);
ShowConnects(db, 6);
// traverse
Walk(db, v1);
Walk(db, v2);
Walk(db, v3);
Walk(db, v4);
Walk(db, v5);
Walk(db, v6);
Walk(db, 1);
Walk(db, 2);
Walk(db, 3);
Walk(db, 4);
Walk(db, 5);
Walk(db, 6);
// shortest path
FindWay(db, v1, v2);
FindWay(db, v1, v3);
FindWay(db, v1, v4);
FindWay(db, v1, v5);
FindWay(db, v1, v6);
// close
db.Close();
}
}
}
The pyorient OrientDB driver for Python is available via pip.
Note that this driver seems to only work with 2.x server - not with 3.x server.
import pyorient
def vstring(v):
return '%d (%s)' % (v.id, v.text)
def show_connects(db, v):
print('Node %s is connected to:' % (vstring(v)))
for vc in db.query("SELECT EXPAND(BOTH('E')) FROM %s" % (v._rid)):
print(' Node %s' % (vstring(vc)))
def show_connects_help(db, id):
show_connects(db, db.query("SELECT FROM V where id = '%s'" % (id))[0])
def walk(db, v):
res = 'Walk: '
first = True
for vc in db.query("TRAVERSE OUT() FROM %s" % (v._rid)):
if first:
first = False
else:
res = res + ' -> '
res = res + vstring(vc)
print(res)
def walk_help(db, id):
walk(db, db.query("SELECT FROM V where id = '%s'" % (id))[0])
def find_way(db, vfrom, vto):
res = 'Find: '
first = True
for link in db.query("SELECT SHORTESTPATH(%s,%s) AS path UNWIND path" % (vfrom._rid, vto._rid)):
vc = db.record_load(link.path)
if first:
first = False
else:
res = res + ' -> '
res = res + vstring(vc)
print(res)
# open
db = pyorient.OrientDB('192.168.60.129', 2424)
db.db_open('test', 'admin', 'hemmeligt', pyorient.DB_TYPE_GRAPH)
# put
v1 = db.command("CREATE VERTEX V SET id = 1, text = 'This is V1'")[0]
v2 = db.command("CREATE VERTEX V SET id = 2, text = 'This is V2'")[0]
v3 = db.command("CREATE VERTEX V SET id = 3, text = 'This is V3'")[0]
v4 = db.command("CREATE VERTEX V SET id = 4, text = 'This is V4'")[0]
v5 = db.command("CREATE VERTEX V SET id = 5, text = 'This is V5'")[0]
v6 = db.command("CREATE VERTEX V SET id = 6, text = 'This is V6'")[0]
e12 = db.command("CREATE EDGE E FROM %s TO %s" % (v1._rid, v2._rid))[0]
e23 = db.command("CREATE EDGE E FROM %s TO %s" % (v2._rid, v3._rid))[0]
e24 = db.command("CREATE EDGE E FROM %s TO %s" % (v2._rid, v4._rid))[0]
e35 = db.command("CREATE EDGE E FROM %s TO %s" % (v3._rid, v5._rid))[0]
e36 = db.command("CREATE EDGE E FROM %s TO %s" % (v3._rid, v6._rid))[0]
e56 = db.command("CREATE EDGE E FROM %s TO %s" % (v5._rid, v6._rid))[0]
# query connections
show_connects(db, v1)
show_connects(db, v2)
show_connects(db, v3)
show_connects(db, v4)
show_connects(db, v5)
show_connects(db, v6)
show_connects_help(db, 1)
show_connects_help(db, 2)
show_connects_help(db, 3)
show_connects_help(db, 4)
show_connects_help(db, 5)
show_connects_help(db, 6)
# traverse
walk(db, v1);
walk(db, v2);
walk(db, v3);
walk(db, v4);
walk(db, v5);
walk(db, v6);
walk_help(db, 1);
walk_help(db, 2);
walk_help(db, 3);
walk_help(db, 4);
walk_help(db, 5);
walk_help(db, 6);
# shortest path
find_way(db, v1, v2);
find_way(db, v1, v3);
find_way(db, v1, v4);
find_way(db, v1, v5);
find_way(db, v1, v6);
An OrientDB driver for PHP is available via composer:
php composer.phar require "ostico/phporient"
Note that this driver seems to only work with 2.x server - not with 3.x server.
<?php
require "vendor/autoload.php";
function vstring($v) {
return sprintf('%d (%s)', $v->id, $v->text);
}
function show_connects($db, $v) {
echo sprintf("Node %s is connected to:\r\n", vstring($v));
foreach($db->query(sprintf("SELECT EXPAND(BOTH('E')) FROM %s", $v->getRid())) as $vc) {
echo sprintf(" Node %s\r\n", vstring($vc));
}
}
function show_connects_help($db, $id) {
show_connects($db, $db->query(sprintf("SELECT FROM V where id = '%s'", $id))[0]);
}
function walk($db, $v) {
echo 'Walk: ';
$first = true;
foreach($db->query(sprintf("TRAVERSE OUT() FROM %s", $v->getRid())) as $vc) {
if($first) {
$first = false;
} else {
echo ' -> ';
}
echo vstring($vc);
}
echo "\r\n";
}
function walk_help($db, $id) {
walk($db, $db->query(sprintf("SELECT FROM V where id = '%s'", $id))[0]);
}
function find_way($db, $vfrom, $vto) {
echo 'Find: ';
$first = true;
foreach($db->query(sprintf("SELECT SHORTESTPATH(%s,%s) AS path UNWIND path", $vfrom->getRid(), $vto->getRid())) as $link) {
$vc = $db->recordLoad($link->path)[0];
if($first) {
$first = false;
} else {
echo ' -> ';
}
echo vstring($vc);
}
echo "\r\n";
}
use PhpOrient\PhpOrient;
// open
$db = new PhpOrient('192.168.60.129', 2424);
$db->dbOpen('test', 'admin', 'hemmeligt');
$db->command("DELETE Edge E");
$db->command("DELETE Vertex V");
// put
$v1 = $db->command("CREATE VERTEX V SET id = 1, text = 'This is V1'");
$v2 = $db->command("CREATE VERTEX V SET id = 2, text = 'This is V2'");
$v3 = $db->command("CREATE VERTEX V SET id = 3, text = 'This is V3'");
$v4 = $db->command("CREATE VERTEX V SET id = 4, text = 'This is V4'");
$v5 = $db->command("CREATE VERTEX V SET id = 5, text = 'This is V5'");
$v6 = $db->command("CREATE VERTEX V SET id = 6, text = 'This is V6'");
$e12 = $db->command(sprintf("CREATE EDGE E FROM %s TO %s", $v1->getRid(), $v2->getRid()));
$e23 = $db->command(sprintf("CREATE EDGE E FROM %s TO %s", $v2->getRid(), $v3->getRid()));
$e24 = $db->command(sprintf("CREATE EDGE E FROM %s TO %s", $v2->getRid(), $v4->getRid()));
$e35 = $db->command(sprintf("CREATE EDGE E FROM %s TO %s", $v3->getRid(), $v5->getRid()));
$e36 = $db->command(sprintf("CREATE EDGE E FROM %s TO %s", $v3->getRid(), $v6->getRid()));
$e56 = $db->command(sprintf("CREATE EDGE E FROM %s TO %s", $v5->getRid(), $v6->getRid()));
// query connections
show_connects($db, $v1);
show_connects($db, $v2);
show_connects($db, $v3);
show_connects($db, $v4);
show_connects($db, $v5);
show_connects($db, $v6);
show_connects_help($db, 1);
show_connects_help($db, 2);
show_connects_help($db, 3);
show_connects_help($db, 4);
show_connects_help($db, 5);
show_connects_help($db, 6);
// traverse
walk($db, $v1);
walk($db, $v2);
walk($db, $v3);
walk($db, $v4);
walk($db, $v5);
walk($db, $v6);
walk_help($db, 1);
walk_help($db, 2);
walk_help($db, 3);
walk_help($db, 4);
walk_help($db, 5);
walk_help($db, 6);
// shortest path
find_way($db, $v1, $v2);
find_way($db, $v1, $v3);
find_way($db, $v1, $v4);
find_way($db, $v1, $v5);
find_way($db, $v1, $v6);
?>
Graph databases are niche for a reason. They are not suited for any type of data. But for the specific cases where the link navigation is central, then using a graph database can be a log easier and faster than using another database type.
Version | Date | Description |
---|---|---|
1.0 | January 22nd 2023 | Initial version |
See list of all articles here
Please send comments to Arne Vajhøj
I am a bit ambigious about OrientDB.
On the positive side:
On the negative side:
There is a lot to like and a lot not to like.