Scala API
There's no specific API written in Scala for OrientDB, but since there's a Java API it'easy to use that one to access OrientDB from Scala, and if needed to write wrappers around it for making a more Scala-like API.
Here we just explain how to start using Scala for doing some basic operations on OrientDB, based on this GitHub repository: OrientDbScalaExample that uses the Graph API. To fully leverage the features of the API, refer to the Java documentation.
Java method invocation problems
Usually the main problems are related to the difference in calling conventions between Scala and Java. Attention must be paid when passing parameters to methods with varargs like OrientGraph.addVertex(...)
, because if not converted to java's repeated args correctly it will cause a compilation problem.
More detailed info here: http://stackoverflow.com/questions/3022865/calling-java-vararg-method-from-scala-with-primitives
http://stackoverflow.com/questions/1008783/using-varargs-from-scala
build.sbt
Let's start defining the build.sbt. All we need is a library dependency:
libraryDependencies ++= Seq(
"com.orientechnologies" % "orientdb-graphdb" % "2.2.0",
)
Creating/Opening a Database
We can start creating/opening a database:
val uri: String = "plocal:target/database/scala_sample"
val graph: OrientGraph = new OrientGraph(uri)
If the database at the specified uri is existing, it will be just opened; if it's not existing, it will be created and opened.
Creating new classes
If we need to define new classes, we can use the createVertexType() method of the OrientGraph class:
val person: OrientVertexType = graph.createVertexType("Person")
person.createProperty("firstName", OType.STRING)
person.createProperty("lastName", OType.STRING)
we can define as many properties we need, each one belonging to a Type (the second parameter of the createProperty() method).
In the same way, we can extend edges, like in this example:
val work: OrientEdgeType = graph.createEdgeType("Work")
work.createProperty("startDate", OType.DATE)
work.createProperty("endDate", OType.DATE)
Adding vertices
If we need to add Vertices, we can use the OrientGraph.addVertex() method, like in this example:
val johnDoe: Vertex = graph.addVertex("class:Person", Nil: _*)
johnDoe.setProperty("firstName", "John")
johnDoe.setProperty("lastName", "Doe")
As seen before in the paragraph "Java method invocation problems", the second parameter of the addVertex() method (the Nil:*_) is needed to tell the Scala compiler which of the overloaded version of the method to use. If not supplied, the call will not compile.
Luckily, the properties of the vertex can be specified in the constructor itself:
val johnSmith: Vertex = graph.addVertex("class:Person", "firstName", "John", "lastName", "Smith")
Adding edges
If we need to add edges, we can use the OrientGraph.addEdge() method:
val johnDoeAcme: Edge = graph.addEdge(null, johnDoe, acme, WorkEdgeLabel)
johnDoeAcme.setProperty("startDate", "2010-01-01")
johnDoeAcme.setProperty("endDate", "2013-04-21")
The first parameter of the call addEdge() is the RID, which is automatically generated by OrientDB and hence not needed. Unfortunately the edge has no overloaded constructor to pass all the properties in one call. Another way to add an edge is starting form a vertex, like in this case:
val johnSmithAcme: Edge = johnSmith.addEdge(WorkEdgeLabel, acme)
johnSmithAcme.setProperty("startDate", "2009-01-01")
In this case we're connecting the johnSmith vertex to the acme company directly using the addEdge() method defined in the Vertex interface.
Querying the database
We can access our data using the OrientGraph.command() method:
val results: OrientDynaElementIterable = graph
.command(new OCommandSQL(s"SELECT expand(in('Work')) FROM Company WHERE name='ACME'"))
.execute()
that will return an Iterable containing the results. In this case, the query finds out all the vertices of the Person class that have a Work relationship with the acme company (or - in other words - all the vertices that have edges labeled Work going out to vertices of class Company that have the property name set to acme); the iterable contains as many elements as the vertices matching the query. In this code sample, we can see how to access the edges of a vertex while iterating on the results:
results.foreach(v => {
// gets the person
val person = v.asInstanceOf[OrientVertex]
// checks if the out edge Work contains the "endDate" property
val workEdgeIterator = person.getEdges(Direction.OUT, WorkEdgeLabel).iterator()
val status = if (!workEdgeIterator.isEmpty && workEdgeIterator.next().getProperty("endDate") != null) "retired" else "active"
println(s"Name: ${person.getProperty("lastName")}, ${person.getProperty("firstName")} [${status}]")
})
since the GraphAPI is Tinkerpop compliant, we have to cast every element of the Iterable to the implementation of Tinkerpop supplied by OrientDB (OrientVertex), and from this object access the edges.
Using SQL
Using SQL commands is straightforward:
graph.command(new OCommandSQL("DELETE VERTEX V")).execute()
where graph is an instance of OrientGraph class.
OrientDbSample.scala
This is the complete file:
import com.orientechnologies.orient.core.metadata.schema.OType
import com.orientechnologies.orient.core.sql.OCommandSQL
import com.tinkerpop.blueprints.{Direction, Edge, Vertex}
import com.tinkerpop.blueprints.impls.orient._
import scala.collection.JavaConversions._
object OrientDbSample extends App {
val WorkEdgeLabel = "Work"
// opens the DB (if not existing, it will create it)
val uri: String = "plocal:target/database/scala_sample"
val graph: OrientGraph = new OrientGraph(uri)
try {
// if the database does not contain the classes we need (i.e. it was just created),
// then adds them
if (graph.getVertexType("Person") == null) {
// we now extend the Vertex class for Person and Company
val person: OrientVertexType = graph.createVertexType("Person")
person.createProperty("firstName", OType.STRING)
person.createProperty("lastName", OType.STRING)
val company: OrientVertexType = graph.createVertexType("Company")
company.createProperty("name", OType.STRING)
company.createProperty("revenue", OType.LONG)
// we now extend the Edge class for a "Work" relationship
// between Person and Company
val work: OrientEdgeType = graph.createEdgeType(WorkEdgeLabel)
work.createProperty("startDate", OType.DATE)
work.createProperty("endDate", OType.DATE)
}
else {
// cleans up the DB since it was already created in a preceding run
graph.command(new OCommandSQL("DELETE VERTEX V")).execute()
graph.command(new OCommandSQL("DELETE EDGE E")).execute()
}
// adds some people
// (we have to force a vararg call in addVertex() method to avoid ambiguous
// reference compile error, which is pretty ugly)
val johnDoe: Vertex = graph.addVertex("class:Person", Nil: _*)
johnDoe.setProperty("firstName", "John")
johnDoe.setProperty("lastName", "Doe")
// we can also set properties directly in the constructor call
val johnSmith: Vertex = graph.addVertex("class:Person", "firstName", "John", "lastName", "Smith")
val janeDoe: Vertex = graph.addVertex("class:Person", "firstName", "Jane", "lastName", "Doe")
// creates a Company
val acme: Vertex = graph.addVertex("class:Company", "name", "ACME", "revenue", "10000000")
// creates edge JohnDoe worked for ACME
val johnDoeAcme: Edge = graph.addEdge(null, johnDoe, acme, WorkEdgeLabel)
johnDoeAcme.setProperty("startDate", "2010-01-01")
johnDoeAcme.setProperty("endDate", "2013-04-21")
// another way to create an edge, starting from the source vertex
val johnSmithAcme: Edge = johnSmith.addEdge(WorkEdgeLabel, acme)
johnSmithAcme.setProperty("startDate", "2009-01-01")
// prints all the people who works/worked for ACME
val res: OrientDynaElementIterable = graph
.command(new OCommandSQL(s"SELECT expand(in('${WorkEdgeLabel}')) FROM Company WHERE name='ACME'"))
.execute()
println("ACME people:")
res.foreach(v => {
// gets the person
val person = v.asInstanceOf[OrientVertex]
// checks if the out edge Work contains the "endDate" property
val workEdgeIterator = person.getEdges(Direction.OUT, WorkEdgeLabel).iterator()
val status = if (!workEdgeIterator.isEmpty && workEdgeIterator.next().getProperty("endDate") != null) "retired" else "active"
println(s"Name: ${person.getProperty("lastName")}, ${person.getProperty("firstName")} [${status}]")
})
}
finally {
graph.shutdown()
}
}