Scala API

There's no specific API written in Scala for OrientDB, but since there's a Java API it's 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

http://stackoverflow.com/questions/3856536/how-to-pass-a-string-scala-vararg-to-a-java-method-using-scala-2-8

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 factory: OrientGraphFactory = new OrientGraphFactory(uri)
    val graph: OrientGraph = factory.getTx()

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)
    work.createProperty("projects", OType.LINKSET)

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, "Work")
    johnDoeAcme.setProperty("startDate", "2010-01-01")
    johnDoeAcme.setProperty("endDate", "2013-04-21")
    johnDoeAcme.setProperty("projects", Set(acmeGlue, acmeRocket))

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. The last line inserts - as a property of the edge - a LinkSet, in this case a Set built with a couple of vertices (see full code below for the definition of those vertices).

Another way to add an edge is starting form a vertex, like in this case:

    val johnSmithAcme: Edge = johnSmith.addEdge("Work", 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]

        // gets the "Work" edge
        val workEdgeIterator = person.getEdges(Direction.OUT, "Work").iterator()
        val edge = workEdgeIterator.next()

        // and retrieves info to print
        val status = if (edge.getProperty("endDate") != null) "retired" else "active"
        val projects = if (edge.getProperty("projects") != null)
          edge.getProperty("projects").asInstanceOf[Set[Vertex]].map(v=>v.getProperty[String]("name")).mkString(", ") else "Any"

        println(s"Name: ${person.getProperty("lastName")}, ${person.getProperty("firstName")} [${status}]. Worked on: ${projects}.")
    })

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)

            val product: OrientVertexType = graph.createVertexType("Project")
            product.createProperty("name", OType.STRING)

            // 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)
            work.createProperty("projects", OType.LINKSET)
        }
        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 a couple of projects
        val acmeGlue = graph.addVertex("class:Project", "name", "ACME Glue")
        val acmeRocket = graph.addVertex("class:Project", "name", "ACME Rocket")

        // 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")
        johnDoeAcme.setProperty("projects", Set(acmeGlue, acmeRocket))

        // 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]

            // gets the "Work" edge
            val workEdgeIterator = person.getEdges(Direction.OUT, WorkEdgeLabel).iterator()
            val edge = workEdgeIterator.next()

            // and retrieves info to print
            val status = if (edge.getProperty("endDate") != null) "retired" else "active"
            val projects = if (edge.getProperty("projects") != null)
              edge.getProperty("projects").asInstanceOf[Set[Vertex]].map(v=>v.getProperty[String]("name")).mkString(", ") else "Any"

            println(s"Name: ${person.getProperty("lastName")}, ${person.getProperty("firstName")} [${status}]. Worked on: ${projects}.")
        })
    }
    finally {
        graph.shutdown()
    }
}