Hot questions for Using Neo4j in scala

Question:

I am struggling to create a node in Neo4J using Cypher from my Java (Scala) code. Need help.

I have an application class:

case class Team (val name: String,
             val contactEmail: String)

and a corresponding jsonifying helper function:

def toNodeProperties = {
   ("props" ->
      ("name"           -> name) ~
      ("contactEmail"   -> contactEmail)
   )
} // Using json4s

Now, I want to create a Node on a Neo4J instance from an instance of this. So, this is what I have tried:

val t = pretty(render(Team("myTeam","team@gmail.com").toNodeProperties))

When I print 't', I see this:

{
  "props" : {
    "name" : "myTeam",
    "contactEmail" : "team@gmail.com",
  }
}

Then, I try to create a statement, thus:

val cypherCreateCommand = "CREATE (t:TestEntity  " + t + ")"
val v1 = db.execute(cypherCreateCommand)

Runtime expresses its displeasure:

Invalid input '"': expected whitespace, comment, a property key name, '}', an identifier or UnsignedDecimalInteger (line 2, column 3 (offset: 26)) " "props" : {" ^

I have thought a JSONIfied string as a parameter to CREATE should have been the obvious thing, but it seemingly is not. Can someone please point out the mistake I am making?

Just in case, here is the relevant portion of pom.xml:

<properties>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <encoding>UTF-8</encoding>
    <scala.tools.version>2.10</scala.tools.version>
    <scala.version>2.10.5</scala.version>
</properties>

  <dependency>
      <groupId>org.json4s</groupId>
      <artifactId>json4s-jackson_2.10</artifactId>
      <version>3.2.11</version>
  </dependency>

  <dependency>
      <groupId>org.json4s</groupId>
      <artifactId>json4s-native_2.10</artifactId>
      <version>3.2.11</version>
  </dependency>


  <dependency>
    <groupId>org.scala-lang</groupId>
    <artifactId>scala-library</artifactId>
    <version>${scala.version}</version>
  </dependency>

<dependency>
  <groupId>joda-time</groupId>
  <artifactId>joda-time</artifactId>
  <version>2.7</version>
</dependency>
  <dependency>
      <groupId>com.github.nscala-time</groupId>
      <artifactId>nscala-time_2.10</artifactId>
      <version>2.2.0</version>
  </dependency>

<dependency>
  <groupId>org.neo4j</groupId>
  <artifactId>neo4j</artifactId>
  <version>2.2.5</version>
</dependency>


<!-- Test -->
  <dependency>
      <groupId>org.scalatest</groupId>
      <artifactId>scalatest_2.10</artifactId>
      <version>2.2.5</version>
  </dependency>
  <dependency>
      <groupId>org.neo4j</groupId>
      <artifactId>neo4j-kernel</artifactId>
      <version>2.2.5</version>
      <type>test-jar</type>
      <scope>test</scope>
  </dependency>
  // .... and the rest

Answer:

First of all - you should use GraphDatabaseService::execute(String query, Map<String, Object> params) method, if you have parametrized query

Using this method (instead of string concatenation) give possibility Neo4j to more efficiently compile query and then reuse it.


For your case I believe this should work:

(I am not strong in Scala, that why this is Java code. But, I think it should be self-explanatory)

Map<String, Object> params = new HashMap<>();
params.put("props", new HashMap<String, Object>() {
    add("name", name);
    add("contactEmail", contactEmail);
});

String query = "CREATE (t:TestEntity  {props})"
db.execute(query, params);

Note: I have not tested this code.

What we have been done:

  • Create params container
  • Put props map into params container
  • Write query with special placeholder (similar to prepared statements in relational DB)
  • Execute query with specified params

EDIT (by @Nirmalya): detailed description about cypher parameters from neo4j docs.

Question:

Is it possible to specify the type of a list when using asList from the Neo4j driver ?

I am using the Java Driver and trying to convert the Java Datatypes to Scala types.

I want something like this:

val srcVersions: List[String] = depLink.get("srcVersions").asList()

where List[String] is a scala type and asList() returns util.List[AnyRef]

Either there is something built in the driver to change the AnyRef to String or I need to iterate through the List to convert each element to a String and then convert the list to scala with scala.collection.JavaConversions._

Thank you.


Answer:

I was a bit quick to ask that one. Found the answer by reading the API doc.

The asList function accepts a Function[Value, String] to make the conversion for each element. So first step is to convert to util.List[String] and then to use scala.collection.JavaConversions._ to convert to List[String]

import scala.collection.JavaConversions._

val srcVersions: List[String] = depLink.get("srcVersions")
                                       .asList(Values.ofToString)
                                       .toList

Question:

Like a lot of people with a Simlar Problem, I can''t get my plugin extention to show up:

{
  "extensions" : {
  },
  "node" : ...
}
Tried

Compiled GetAll.java using java 1.7. Using SBT, build.sbt:

javaOptions ++= Seq( "-source", "1.7" )
libraryDependencies ++= Seq(
  "org.neo4j" % "neo4j" % "2.1.3" % "provided" withSources(),
  "org.neo4j" % "server-api" % "2.1.3" % "provided" withSources()
)

Very simple JAR:

META-INF/
META-INF/MANIFEST.MF
META-INF/services/
META-INF/services/org.neo4j.server.plugins.ServerPlugin
GetAll.class

Simple GetAll.class:

package com.danmacias.neo4j.plugin;
import java.util.ArrayList;
import java.util.List;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.server.plugins.Description;
import org.neo4j.server.plugins.Name;
import org.neo4j.server.plugins.PluginTarget;
import org.neo4j.server.plugins.ServerPlugin;
import org.neo4j.server.plugins.Source;
import org.neo4j.tooling.GlobalGraphOperations;

// START SNIPPET: GetAll
package com.danmacias.neo4j.plugin;
import java.util.ArrayList;
import java.util.List;
@Description( "An extension to the Neo4j Server for getting all nodes or relationships" )
public class GetAll extends ServerPlugin
{
  @Name( "get_all_nodes" )
  @Description( "Get all nodes from the Neo4j graph database" )
  @PluginTarget( GraphDatabaseService.class )
  public Iterable<Node> getAllNodes( @Source GraphDatabaseService graphDb )
  {
    ArrayList<Node> nodes = new ArrayList<>();
    try (Transaction tx = graphDb.beginTx())
    {
      for ( Node node : GlobalGraphOperations.at( graphDb ).getAllNodes() )
      {
        nodes.add( node );
      }
      tx.success();
    }
    return nodes;
  }
}

META-INF/services/org.neo4j.server.plugins.ServerPlugin:

com.danmacias.neo4j.plugin.GetAll

(note the ending newline)

Using neo4j 2.0.2 java version "1.7.0_71" Java(TM) SE Runtime Environment (build 1.7.0_71-b14) Java HotSpot(TM) 64-Bit Server VM (build 24.71-b01, mixed mode)

This issue has me on the verge of going insane :S


Answer:

Your GetAll class should be in a directory in the Jar:

com/danmacias/neo4j/plugin/GetAll.class

Like usually Java jars are packaged.

Question:

I am trying to use scala over the embedded jave Neo4j api. I am having trouble opening the database for reading on subsequent occasions. The code below should create two nodes and an edge every time it runs, but return all of them at the begining of each run. So, 0 nodes first time, 2 nodes second time, 4 third time etc.

import org.neo4j.tooling.GlobalGraphOperations
import org.neo4j.graphdb.factory.GraphDatabaseFactory
import org.neo4j.graphdb.RelationshipType

object tester extends App{

  val DB_PATH = "data/neo4j"
  object KNOWS extends RelationshipType {
    override def name(): String = "KNOWS"
  }
  val graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(DB_PATH)  //seems to reset the whole directory
  println(graphDb)
  try {
    println("Begin")
    val tx = graphDb.beginTx() // Database operations go here
    println(GlobalGraphOperations.at(graphDb).getAllNodes.iterator)
    val nodes = GlobalGraphOperations.at(graphDb).getAllNodes.iterator
    while (nodes.hasNext()) {
      println(nodes.next())
    }
    nodes.close()
    val relT = GlobalGraphOperations.at(graphDb).getAllRelationships.iterator
    while (relT.hasNext()) {
      println(relT.next())
    }
    println("Success - Begin")
    tx.success()
  }
  try {
    val tx = graphDb.beginTx() // Database operations go here
    val firstNode = graphDb.createNode
    val secondNode = graphDb.createNode
    val relationship = firstNode.createRelationshipTo(secondNode, KNOWS)

    println(firstNode)
    println(secondNode)
    println(relationship)
    println(relationship.getType.name)
    tx.success()
    println("Success")
  }
  println("End")
  try {
    val tx = graphDb.beginTx() // Database operations go here
    println(GlobalGraphOperations.at(graphDb).getAllNodes.iterator)
    val nodes = GlobalGraphOperations.at(graphDb).getAllNodes.iterator
    while (nodes.hasNext()) {
      println(nodes.next())
    }
    nodes.close()
    val relT = GlobalGraphOperations.at(graphDb).getAllRelationships.iterator
    while (relT.hasNext()) {
      println(relT.next())
    }
    println("Success - End")
    tx.success()
  }
  graphDb.shutdown()
}

However, every time it simply seems to give an empty database and then the 2 new nodes. What's going on here?

EmbeddedGraphDatabase [data/neo4j]
Begin
org.neo4j.tooling.GlobalGraphOperations$1$1@74c49a90
Success - Begin
Node[2]
Node[3]
Relationship[1]
KNOWS
Success
End
org.neo4j.tooling.GlobalGraphOperations$1$1@2ec0df08
Node[2]
Node[3]
Relationship[1]
Success - End

Process finished with exit code 0

Answer:

This is happening because you are not closing the transaction. You can do this by calling tx.close(). Also I think that instantiating tx inside try is not exactly how it should be. Here is a working version of your program:

import org.neo4j.tooling.GlobalGraphOperations
import org.neo4j.graphdb.factory.GraphDatabaseFactory
import org.neo4j.graphdb.RelationshipType

object tester extends App{

  val DB_PATH = "data/neo4j"
  object KNOWS extends RelationshipType {
    override def name(): String = "KNOWS"
  }
  val graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(DB_PATH)
  println(graphDb)

  val tx1 = graphDb.beginTx() // Database operations go here
  try {
    println("Will list all nodes")
    println("1 - Begin")
    println("GlobalGraphOperations.at(graphDb).getAllNodes.iterator")
    val nodes = GlobalGraphOperations.at(graphDb).getAllNodes.iterator
    while (nodes.hasNext()) {
      println(nodes.next())
    }
    nodes.close()
    val relT = GlobalGraphOperations.at(graphDb).getAllRelationships.iterator
    while (relT.hasNext()) {
      println(relT.next())
    }
    println("1 - Success - Begin")
    tx1.success()
  }
  finally {
    tx1.close()
  }

  val tx2 = graphDb.beginTx() // Database operations go here
  try {
    val firstNode = graphDb.createNode
    val secondNode = graphDb.createNode
    val relationship = firstNode.createRelationshipTo(secondNode, KNOWS)

    println(firstNode)
    println(secondNode)
    println(relationship)
    println(relationship.getType.name)
    tx2.success()
    println("2 - Success")
  }
  finally {
    tx2.close()
  }

  println("2 - End")

  val tx3 = graphDb.beginTx() // Database operations go here
  try {
    println(GlobalGraphOperations.at(graphDb).getAllNodes.iterator)
    val nodes = GlobalGraphOperations.at(graphDb).getAllNodes.iterator
    while (nodes.hasNext()) {
      println(nodes.next())
    }
    nodes.close()
    val relT = GlobalGraphOperations.at(graphDb).getAllRelationships.iterator
    while (relT.hasNext()) {
      println(relT.next())
    }
    println("3 - Success - End")
    tx3.success()
  }
  finally {
    tx3.close()
  }
  graphDb.shutdown()
}
EXTRA

I tried to bring your program closer to the "scala-style". Also, I tried to remove boilerplate and repeated code. To accomplish this I:

  • Used JavaConverters to handle Java collections and Iterables like we handle them in Scala
  • Created a method withTransaction to get automatic resource management for our transaction in scala.

This is the result:

import org.neo4j.tooling.GlobalGraphOperations
import org.neo4j.graphdb.factory.GraphDatabaseFactory
import org.neo4j.graphdb.RelationshipType
import org.neo4j.graphdb.Transaction
import scala.collection.JavaConverters._

object tester extends App{
  val DB_PATH = "data/neo4j"
  object KNOWS extends RelationshipType {
    override def name(): String = "KNOWS"
  }

  def withTransaction (doWithTransaction: Transaction => Unit) { 
    val tempTx = graphDb.beginTx() 

    try {
      doWithTransaction(tempTx)
    }
    finally {
      tempTx.close()
    }
  }

  val graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(DB_PATH)
  println(graphDb)

  withTransaction { tx => 
    println("1 - Begin")
    val nodes = GlobalGraphOperations.at(graphDb).getAllNodes 
    for (node <- nodes.asScala)
      println(node)

    val relTs = GlobalGraphOperations.at(graphDb).getAllRelationships
    for (irelT <- relTs.asScala) 
      println(irelT)

    println("1 - Success - Begin")
    tx.success()
  }


  withTransaction { tx => 
    val firstNode = graphDb.createNode
    val secondNode = graphDb.createNode
    val relationship = firstNode.createRelationshipTo(secondNode, KNOWS)

    println(firstNode)
    println(secondNode)
    println(relationship)
    println(relationship.getType.name)
    tx.success()
    println("2 - Success")
  }

  println("2 - End")

  withTransaction { tx => 
    println(GlobalGraphOperations.at(graphDb).getAllNodes.iterator)

    val nodes = GlobalGraphOperations.at(graphDb).getAllNodes 
    for (node <- nodes.asScala)
      println(node)

    val relTs = GlobalGraphOperations.at(graphDb).getAllRelationships
    for (irelT <- relTs.asScala) 
      println(irelT)

    println("3 - Success - End")
    tx.success()
  }

  graphDb.shutdown()
}