Overview

Spring Data – Part 5: Neo4j

8 Comments

Introduction

Neo4j [1] is a high-performance NoSQL [2] datastore specialized in persisting graphs. A graph [3] is data structure consisting of finite sets of vertices and edges, where an edge is a connection between two vertices. Graphs are used to represent relationsiphs or connections (edges) between domain objects (vertices). Social networks are an application of a graph data structure.

Neo4j

After downloading the community edition of Neo4j [4] you just unzip the distribution package to a folder NEO4J_HOME and start the server with

${NEO4J_HOME}/bin/Neo4J.bat

(on a Windows OS). By default, the web admin interface can be found at http://localhost:7474/webadmin. which includes a web based shell. More important for a start is the “Data brower” tab where you can add nodes and relationship between them. There is also a search function that renders the result graphs.

Spring Data Neo4j

First of all, we let Maven download the latest stable release version of Spring Data Neo4j:

<dependency>
   <groupId>org.springframework.data</groupId>
   <artifactId>spring-data-neo4j</artifactId>
   <version>2.0.0.RELEASE</version>
</dependency>

We want to access our Neo4j server via remote REST calls, so we have to add this optinal dependency:

<dependency>
   <groupId>org.springframework.data</groupId>
   <artifactId>spring-data-neo4j-rest</artifactId>
   <version>2.0.0.RELEASE</version>
</dependency>

As with all the Spring Data projects, configuration is quite simple by special XML name spaces. In our case neo4j does the job:

<!-- REST Connection to Neo4j server -->
<bean id="restGraphDatabase" class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase">
  <constructor-arg value="http://localhost:7474/db/data/" />
</bean>
 
<!-- Neo4j configuration (creates Neo4jTemplate) -->
<neo4j:config graphDatabaseService="restGraphDatabase" />
 
<!-- Package w/ automagic repositories -->
<neo4j:repositories base-package="neo4j" />

We provide the base URL for the REST access and wire this service into the Neo4j configuration.

The basic idea behind the Spring Data project is to write only interface methods to define your custom finders. Spring will inject an appropriate implementation at runtime which also provides all CRUD functionality. To enable this in your application, you have to configure the package with the <neo4j:repositories ...> node.

Domain Object

As in the previous posts of this series we’ll persist some simple user objects. The Neo4j flavour of this class may look like this:

/** Simple user class. */
@NodeEntity public class User {
 
  @GraphId Long id;
 
  @Indexed private String login;
 
  private String fullName;
 
  private Date lastLogin;
 
  @RelatedTo(type = "knows", direction = Direction.OUTGOING)
  Set<User> friends;	
  ...

To enable persistence with Neo4j, you use the @NodeEntity at the class level. The unique id of every node has to be annotated with @GraphId. You don’t have to care about the numbering, Neo4j allocates the values. Indices can be define by using the @Indexed annotation.

To define a basic relation between the users named knows (expressing that user U knows user V), we used the annotation @RelatedTo at a set of users.

The following example is based on a graph of users. We’ll have n users. Each user U_i knows user U_j (for all 0 <= i < j <= n). User root knows them all. For n = 3 the graph looks like this:

There is also a standalone RCP application called Neoclipse [6] for graph visualization.

Repository

To define a simple repository with all the CRUD methods we need a single line of code:

public interface UserRepository extends GraphRepository<User> {}

We’ll add some custom finders later. Right now, we persist the above graph by using the method save:

...
// build graph
for ( int i = 0; i < user.length; i++ ) {
  root.knows(user[i]);
  for ( int j = i; j < user.length; j++ ) {
    user[i].knows(user[j]);
  }
}
 
// save nodes
for ( int i = 0; i < user.length; i++ ) {
  repo.save(user[i]);
}
repo.save( root );

We added a convinience method knows(...) to the user class which adds the given user to the set of known users to make the code better readable. If you know the id of a saved node, you can display it in your browser (actually, it’s a REST get call with HTML output):

http://localhost:7474/db/data/node/98

To find a single node via the Spring Data API, use the standard finder method findOne(long):

User root = repo.findOne(rootId);

Time to add some custom finders to our repository:

public interface UserRepository extends GraphRepository<User> {
 
  User findByLogin(String login);
 
  @Query("START root=node:User(login = 'root') MATCH root-[:knows]->friends RETURN friends")
  List<User> findFriendsOfRoot();
}

The first query returns a single node by matching the node’s login property. It is also possible to write your own Cypher [5] queries with the @Query annotation. The second query does so and returns all users known by the root user.

The complete source code of the example can be found a github.

Summary

That’s it for today. I showed you how to install and run your standalone Neo4j server instance. We set up and configured a Maven based Spring Data Neo4j project. After persisting a small example graph, we wrote some custom finders and even used the Cypher query language.

Of course, this was only a short glimpse into the Neo4j universe. I hope you enjoyed it. Browse the Neo4j site to discover more, including embedded database with transaction support and geospatial queries.

Spring Data Project

These are my other posts covering the Spring Data project:

Part 4: Geospatial Queries with Spring Data Mongo DB
Part 3: Spring Data Mongo DB
Part 2: Spring Data JPA
Part 1: Spring Data Commons

Expect upcoming blog posts on Spring Data Redis and Spring GemFire

References

[1] Neo4j
[2] NoSQL databases
[3] Graphs
[4] Neo4j Download
[5] Cypher Cookbook
[6] Neoclipse Download

More content about Architecture

Kommentare

  • Great post! In the findFriendsOfRoot(), how can I parameterize it so that I can retrieve friends of any node? I’d like to use findFriendsofNode(String login). Thanks!

  • 3. March 2014 von Tomek

    Could you explain me how Can I do some operation on database from not test class?

    • Tobias Trelle

      The mapping in the domain object and repository implementation will be exactly the same.

      When using production code (assuming your are using the Spring framework), you autowire the repo the same way you do in the test code:

      @Autowired
      UserRepository repo;
      
      • 4. March 2014 von Tomek

        That means that I must use spring framework not only spring data neo4j? How Spring Data Neo4j is related to Spring framework?

        In my class (not test) i should past
        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration
        ?
        This code tells my class that it can you some neo4j spring data operations?

  • 8. October 2014 von Prabjot Singh

    How spring repository creates dynamic finder methods. i mean, if i write repo.findByName ,then i got result.then if i write repo.getByName..then i got result. so how can i have my custom methods ..any proper way to write this ?

    • Tobias Trelle

      Dear Prabjot,

      the last example I provided demonstrates how to write custom finders:


      @Query(“START root=node:User(login = {0}) MATCH root-[:knows]->friends RETURN friends”)
      List findFriendsofNode(String login);

      You use the @Query annotation and provide a query in the Cypher query language.

      HTH, Tobias

Comment

Your email address will not be published. Required fields are marked *