Hot questions for Spring Data Neo4j

Top 10 Java Open Source / Spring / Spring Data Neo4j


We have a project where we use Spring Data Neo4J. One of the important entities is shown below:

public class Category {
    Long id;

    String name;

    @RelatedTo(direction = Direction.INCOMING, type = "CHILD")
    Category parent;

    @RelatedTo(direction = Direction.OUTGOING, type = "CHILD")
    Set<Category> children;

We have a requirement to find out all the leaf categories (that is, categories without any children) starting from a specific category whose name is known. For example, given the hierarchy shown below:

        Point and Shoot
        Office tables
        Home tables
        Lounge chairs
        Office chairs

a search for "Furniture" should return "Office tables", "Home tables", "Lounge chairs" and "Office chairs". Similarly, a search for "Computing" should return "Desktop", "Laptop", "Tablet" and "Netbook".

Need help in creating a cypher query that can be placed on a Spring Data repository method to give me all the leaf nodes starting from the specified node.

EDIT The following query (with the associated Spring Data repository method) worked after help from Wes:

"START  category=node:__types__(className='org.example.domain.Category') " +
"MATCH  category-[:CHILD*0..]->child " +
"WHERE{0} AND NOT(child-[:CHILD]->()) " +
"RETURN child")
List<Category> findLeaves(String name);


This is the simplest way I've found with cypher:

start n=node(*) // you can specify a single node here if you want
match n-[r*]->m
where not(m-->()) // this limits m to be only leaf nodes 
return distinct m; // this returns the distinct leaf nodes (not necessary if there are only simple paths)

Edit: (since people are recently upvoting this... here's an update using 3.x cypher)

match (n) 
where not (n)-->() 
return distinct n


How to search node using cypher query where one of the node property having array of string ? e.g. members-- > ["abc","xyz","pqr"]. I can find the node by keeping order of array elements in same manner, for e.g.

START root=node(*) where has(root.members) and root.members=["abc","xyz","pqr"] return root;

but How to search node if I do not / cannot provide the exact order of elements as they are in node property ?


Use the "all" predicate to ensure every element in the root.member is in the given list, i.e. root.members is a subset of the given list, and the length expression ensures that the given list has no more elements than what is in the root.members, so both of them contain the exactly same members.

START root=node(*)
Where has(root.members) and all ( m in root.members where m in ["abc","xyz","pqr"]) and length(root.members) = length(["abc","xyz","pqr"])
Return root


CONTEXT : I am developing a java Spring Boot system backed by a neo4j database. I access the database using the "ClassRepo extends GraphRepository" structure. Writing queries is a simple case of hard coding in my precise query and replacing a specified part of it with a supplied parameter (in this case courseName).

@Query("MATCH (node:Course) WHERE = {courseName}  RETURN node LIMIT 1")
Course findByName(@Param("courseName") String name);

This has all worked fine for me, allowing me to return one, or many results without issue. However, as my project has developed I now offer a large list of options to search by (faceted search, think amazon product filters). It seems silly to write a static cipher query for each and every permutation of chosen, or not chosen filtering options.

My solution (attempt) was to pass in parts of the query as parameters, in essence making a string query builder :

@Query("MATCH (course:Course) -[r]-> (description:CourseYearDescription) " +
        "WITH course, count(description) as relationCount, collect(description) as descriptions " +
        "WHERE relationCount > {numberOfYears} {returnCourse}")
Iterable<Course> findCourseWithNumberOfYears(
        @Param("numberOfYears") int numberOfYears,
        @Param("returnCourse") String returnCourse

Where "returnCourse" is a string with the value "RETURN course". I know for a fact that "RETURN course" entered statically in the query string works. I've just removed it and passed the string value in as a parameter to see if it can generate the same query and run it at run time.

This had no real success, returning me to an error page and printing out the following stack:

QUESTION: Is there a way to append/insert strings into a cypher query strings used by a GraphRepository, so that the query can be altered dynamically, ie add a where clause to the end of a match query at run time.


No, there isn't. SDN/Neo4j OGM will not modify custom queries at run time and merely hands them off to be executed by the Cypher engine. Hence, the only things you can parameterize are those that Cypher will allow you to (

Agree that it does not make sense to define multiple @Query statements for each variation, but what you can do is build your query as a string and use the Neo4jOperations.query* methods that will accept your dynamically generated Cypher queries and a map of parameters (again, parameters that are valid in a Cypher statement).

Hope that helps.


I'm trying to create a REST API using Spring Boot (Version 1.4.0.M2) with Spring Data Neo4j, Spring Data Rest and Spring Security. The Domain is consisting of three types of Entities:

  • The user entity storing the username/password and relationships to all user owned entities:
public class User extends Entity {
    @Relationship(type = "OWNS")
    private Set<Activity> activities;
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String password;
    private String username;
  • Content entities created by/owned by a user:
public class Activity extends Entity {
    private String name;
    @Relationship(type = "ON", direction = Relationship.INCOMING)
    private Set<Period> periods;
    @Relationship(type = "HAS", direction = Relationship.INCOMING)
    private User user;
  • Data entities storing date and time informations accessible to all users
public class Period extends Entity {
    @Relationship(type = "HAS")
    private Set<Activity> activities;
    @Relationship(type = "HAS_PERIOD", direction = Relationship.INCOMING)
    private Day day;
    private PeriodNames name;

I'm trying to keep everything as simple as possible for now so I only use Repositories extending GraphRepository and a Spring Security configuration that uses Basic Authentication and the User Object/Repository in the UserDetailsService. This is working as expected, I am able to create a user, create some objects and so on.

The problem is, I want a user only to access their own entities. Currently, everybody can access everything. As I understood it from my research, I have three ways to achieve this:

  1. Use Spring Security Annotations on the repository methods like this:
@PostAuthorize("returnObject.user.username == principal.username")
Activity findOne(Long id);
@PostFilter("filterObject.user.username == principal.username")
Iterable<Activity> findAll();
  1. Annotate the methods with @Query and use a custom query to get the data.
  2. Create custom controllers and services that do the actual data querying, similar to this: sdn4-university .

Now my question:

What would be the best way to implement the desired functionality using my available tools?

For me it seems to be the preferred way to use #2, the custom query. This way I can only fetch the data that I actually need. I would have to try to find a way to create a query that enables paging for Page<Activity> findAll(Pageable pageable) but I hope this is possible. I wasn't able to use principal.username in the custom query though. It seems as if spring-data-neo4j doesn't have support for SpEL right now. Is this correct or is there another way to access the currently authenticated user in a query?

Way #1, using Spring Security Annotations works for me (see the code above) but I could not figure out how to filter Page<Activity> findAll(Pageable pageable) because it returns a Page object and not an entity or collection. Also I'm not sure if this way is efficient as the database always has to query for all entities and not only the ones owned by a specific user. This seems like a waste of resources. Is this incorrect?

Or should I just go with #3 and implement custom controllers and services? Is there another way that I didn't read about?

I'm very grateful for any input on this topic!

Thanks, Daniel


Since no one has answered yet, let me try...

1) I agree with your assessment that using Spring @PostAuthorize Security is not the way to go here. For filtering data it seems not to be the perfect way to do it here. As you mentioned, it would either load all the data and then filter it, creating a heavy load or probably wreck the paging mechanism:

Imagine you have a million results, loading them all would be heavy. And if you filter them later, you might end up with let's say 1.000 valid results. But I strongly doubt that the paging mechanism will be able to cope with that, more likely that in the end you will seem to have many empty pages. So if you loaded the, let's say, first 20 results, you might end up with an empty result, because they were all filtered out.

Perhaps for some stuff you could use @PreAuthorize to prevent a query from happening, if you only want to get a single result, like in findOne. This could lead to a 403, if not allowed, which would, imho, be ok. But for filtering collections, Spring security doesn't seem a good idea.

3) That's always a possibility, but I wouldn't go there without trying for alternatives. Spring Data Rest is intended to make our code cleaner and coding easier, so we should not throw it away without being 100% sure that we cannot get it to do what we need.

2) Thomas Darimont wrote in this blog posting that there is a way to use principal (and other stuff) in @Query annotations. Let me sumarize to have the answer here...

The basic idea is to create a new EvaluationContextExcentionSupport:

class SecurityEvaluationContextExtension extends EvaluationContextExtensionSupport {

  public String getExtensionId() {
    return "security";

  public SecurityExpressionRoot getRootObject() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    return new SecurityExpressionRoot(authentication) {};


class SecurityConfiguration {

    EvaluationContextExtension securityExtension() {
        return new SecurityEvaluationContextExtension();

...which now allows a @Query like this...

@Query("select o from BusinessObject o where o.owner.emailAddress like "+
  "?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")

To me, that seems to be the most clean solution, since your @Query now uses the principal without you having to write all the controllers yourself.


I have the following setup:

spring-boot application (using embedded tomcat)
spring-date-neo4j (embedded mode)

and neo4j-browser included using (the goal is to be able to use REST and web browser to debug embedded database):



And started using:

@Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService(DatabaseConfiguration dbConf) {
    // removed with every maven clear
    return new GraphDatabaseFactory().newEmbeddedDatabase(dbConf.getDbPath());

    // stays after clear
    //return new EmbeddedGraphDatabase("./data/neo4j.db");

@Bean(destroyMethod = "stop")
public WrappingNeoServerBootstrapper serverWrapper(GraphDatabaseService db) {
    WrappingNeoServerBootstrapper wrapper = new WrappingNeoServerBootstrapper((GraphDatabaseAPI)db);
    return wrapper;

And spring boot is insisting on trying Jetty as the servlet container even with the following config annotations for the main app class:

@EnableAutoConfiguration(exclude = EmbeddedServletContainerAutoConfiguration.EmbeddedJetty.class)
public class WebApplication extends WebMvcConfigurerAdapter {

But the jetty version Spring uses (8) does not support web sockets so I am getting

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'jettyEmbeddedServletContainerFactory' defined in class path resource 
Initialization of bean failed; nested exception is java.lang.IllegalStateException:
Websockets are currently only supported in Tomcat (found class org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory).

How do I tell Spring Boot to ignore Jetty and always use Tomcat?

The dependency tree:

[INFO] Building webapp 1.0
[INFO] ------------------------------------------------------------------------
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ webapp ---
[INFO] webapp:war:1.0
[INFO] +- common:jar:1.0:compile
[INFO] |  +- javax.validation:validation-api:jar:1.1.0.Final:compile
[INFO] |  +-
[INFO] |  |  +- org.aspectj:aspectjrt:jar:1.7.4:compile
[INFO] |  |  +-
[INFO] |  |  +- org.neo4j:neo4j-cypher-dsl:jar:2.0.1:compile
[INFO] |  |  +- org.neo4j:neo4j:jar:2.0.3:compile
[INFO] |  |  +- org.slf4j:slf4j-api:jar:1.7.7:compile
[INFO] |  |  \- org.slf4j:jcl-over-slf4j:jar:1.7.7:compile
[INFO] |  +- commons-codec:commons-codec:jar:1.9:compile
[INFO] |  +- org.apache.poi:poi:jar:3.10-FINAL:compile
[INFO] |  +- org.apache.poi:poi-scratchpad:jar:3.10-FINAL:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.3.3:compile
[INFO] |  \- com.fasterxml.jackson.core:jackson-databind:jar:2.3.3:compile
[INFO] |     \- com.fasterxml.jackson.core:jackson-core:jar:2.3.3:compile
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:1.0.2.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter:jar:1.0.2.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot:jar:1.0.2.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot-autoconfigure:jar:1.0.2.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot-starter-logging:jar:1.0.2.RELEASE:compile
[INFO] |  |  |  +- org.slf4j:jul-to-slf4j:jar:1.7.7:compile
[INFO] |  |  |  +- org.slf4j:log4j-over-slf4j:jar:1.7.7:compile
[INFO] |  |  |  \- ch.qos.logback:logback-classic:jar:1.1.2:compile
[INFO] |  |  |     \- ch.qos.logback:logback-core:jar:1.1.2:compile
[INFO] |  |  \- org.yaml:snakeyaml:jar:1.13:runtime
[INFO] |  \- org.springframework.boot:spring-boot-starter-tomcat:jar:1.0.2.RELEASE:compile
[INFO] |     +- org.apache.tomcat.embed:tomcat-embed-el:jar:7.0.52:compile
[INFO] |     \- org.apache.tomcat.embed:tomcat-embed-logging-juli:jar:7.0.52:compile
[INFO] +- org.springframework:spring-core:jar:4.0.3.RELEASE:compile
[INFO] +- org.springframework:spring-context:jar:4.0.3.RELEASE:compile
[INFO] |  \- org.springframework:spring-expression:jar:4.0.3.RELEASE:compile
[INFO] +- org.springframework:spring-beans:jar:4.0.3.RELEASE:compile
[INFO] +-
[INFO] |  +- aopalliance:aopalliance:jar:1.0:compile
[INFO] |  \-
[INFO] +-
[INFO] +-
[INFO] |  \-
[INFO] |     \- org.springframework:spring-jdbc:jar:4.0.3.RELEASE:compile
[INFO] +- org.springframework:spring-webmvc:jar:4.0.3.RELEASE:compile
[INFO] +- org.springframework:spring-web:jar:4.0.3.RELEASE:compile
[INFO] +- org.springframework:spring-websocket:jar:4.0.3.RELEASE:compile
[INFO] +- org.springframework:spring-messaging:jar:4.0.3.RELEASE:compile
[INFO] +- org.apache.tomcat.embed:tomcat-embed-websocket:jar:7.0.52:compile
[INFO] |  \- org.apache.tomcat.embed:tomcat-embed-core:jar:7.0.52:compile
[INFO] +- org.freemarker:freemarker:jar:2.3.20:compile
[INFO] +- org.springframework:spring-context-support:jar:4.0.3.RELEASE:compile
[INFO] +- net.glxn:qrgen:jar:1.4:compile
[INFO] |  \-
[INFO] |     \-
[INFO] +- org.springframework:spring-tx:jar:4.0.3.RELEASE:compile
[INFO] +- org.springframework:spring-aop:jar:4.0.3.RELEASE:compile
[INFO] +- org.springframework:spring-aspects:jar:4.0.3.RELEASE:compile
[INFO] |  \- org.aspectj:aspectjweaver:jar:1.7.4:compile
[INFO] +- org.hibernate:hibernate-validator:jar:5.1.1.Final:compile
[INFO] |  +- org.jboss.logging:jboss-logging:jar:3.1.3.GA:compile
[INFO] |  \- com.fasterxml:classmate:jar:1.0.0:compile
[INFO] +- javax.el:javax.el-api:jar:2.2.4:compile
[INFO] +- org.glassfish.web:javax.el:jar:2.2.4:compile
[INFO] +- org.apache.commons:commons-lang3:jar:3.3.2:compile
[INFO] +- commons-io:commons-io:jar:2.4:compile
[INFO] +- commons-fileupload:commons-fileupload:jar:1.3.1:compile
[INFO] +- org.jsoup:jsoup:jar:1.7.3:compile
[INFO] +- com.sun.jersey:jersey-server:jar:1.18:compile
[INFO] |  +- asm:asm:jar:3.1:compile
[INFO] |  \- com.sun.jersey:jersey-core:jar:1.18:compile
[INFO] +- com.sun.jersey:jersey-servlet:jar:1.18:compile
[INFO] +-
[INFO] |  +- org.neo4j:neo4j:pom:2.0.3:compile
[INFO] |  |  +- org.neo4j:neo4j-lucene-index:jar:2.0.3:compile
[INFO] |  |  |  \- org.apache.lucene:lucene-core:jar:3.6.2:compile
[INFO] |  |  +- org.neo4j:neo4j-graph-algo:jar:2.0.3:compile
[INFO] |  |  +- org.neo4j:neo4j-udc:jar:2.0.3:compile
[INFO] |  |  +- org.neo4j:neo4j-graph-matching:jar:2.0.3:compile
[INFO] |  |  \- org.neo4j:neo4j-jmx:jar:2.0.3:compile
[INFO] |  +- org.neo4j:neo4j-kernel:jar:2.0.3:compile
[INFO] |  |  \- org.apache.geronimo.specs:geronimo-jta_1.1_spec:jar:1.1.1:compile
[INFO] |  +- org.neo4j:server-api:jar:2.0.3:compile
[INFO] |  |  \-
[INFO] |  +- org.neo4j:neo4j-cypher:jar:2.0.3:compile
[INFO] |  |  +- org.neo4j:neo4j-cypher-commons:jar:2.0.3:compile
[INFO] |  |  +- org.neo4j:neo4j-cypher-compiler-1.9:jar:2.0.3:compile
[INFO] |  |  +- org.neo4j:neo4j-cypher-compiler-2.0:jar:2.0.3:compile
[INFO] |  |  |  +- org.parboiled:parboiled-scala_2.10:jar:1.1.6:compile
[INFO] |  |  |  |  \- org.parboiled:parboiled-core:jar:1.1.6:compile
[INFO] |  |  |  \- net.sf.opencsv:opencsv:jar:2.0:compile
[INFO] |  |  +- com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:jar:1.3.1:compile
[INFO] |  |  \- org.scala-lang:scala-library:jar:2.10.3:compile
[INFO] |  +-
[INFO] |  +- org.codehaus.janino:janino:jar:2.6.1:compile
[INFO] |  |  \- org.codehaus.janino:commons-compiler:jar:2.6.1:compile
[INFO] |  +- org.eclipse.jetty:jetty-server:jar:9.0.5.v20130815:compile
[INFO] |  |  +- org.eclipse.jetty:jetty-http:jar:9.0.5.v20130815:compile
[INFO] |  |  \- org.eclipse.jetty:jetty-io:jar:9.0.5.v20130815:compile
[INFO] |  +- org.eclipse.jetty:jetty-webapp:jar:8.1.14.v20131031:compile
[INFO] |  |  +- org.eclipse.jetty:jetty-xml:jar:8.1.14.v20131031:compile
[INFO] |  |  \- org.eclipse.jetty:jetty-servlet:jar:8.1.14.v20131031:compile
[INFO] |  |     \- org.eclipse.jetty:jetty-security:jar:8.1.14.v20131031:compile
[INFO] |  +- commons-configuration:commons-configuration:jar:1.6:compile
[INFO] |  |  +- commons-collections:commons-collections:jar:3.2.1:compile
[INFO] |  |  +- commons-lang:commons-lang:jar:2.4:compile
[INFO] |  |  +- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] |  |  \- commons-beanutils:commons-beanutils-core:jar:1.8.0:compile
[INFO] |  +- commons-digester:commons-digester:jar:1.8.1:compile
[INFO] |  |  \- commons-beanutils:commons-beanutils:jar:1.8.0:compile
[INFO] |  +- org.codehaus.jackson:jackson-jaxrs:jar:1.9.7:compile
[INFO] |  |  +- org.codehaus.jackson:jackson-core-asl:jar:1.9.7:compile
[INFO] |  |  \- org.codehaus.jackson:jackson-mapper-asl:jar:1.9.7:compile
[INFO] |  +- org.rrd4j:rrd4j:jar:2.0.7:compile
[INFO] |  +- org.mozilla:rhino:jar:1.7R4:compile
[INFO] |  +- bouncycastle:bcprov-jdk16:jar:140:compile
[INFO] |  +- com.sun.jersey.contribs:jersey-multipart:jar:1.9:compile
[INFO] |  |  \- org.jvnet:mimepull:jar:1.6:compile
[INFO] |  \- org.neo4j:neo4j-shell:jar:2.0.3:compile
[INFO] |     \- org.apache.servicemix.bundles:org.apache.servicemix.bundles.jline:jar:0.9.94_1:compile
[INFO] +-
[INFO] +- org.eclipse.jetty:jetty-util:jar:9.0.7.v20131107:compile
[INFO] +- com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:jar:r239:compile
[INFO] |  +- (version selected from constraint [11.0,))
[INFO] |  \- (version selected from constraint [1.3.9,))
[INFO] +- junit:junit:jar:4.11:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- org.mockito:mockito-all:jar:1.9.5:test
[INFO] +- org.springframework:spring-test:jar:4.0.3.RELEASE:test
[INFO] +- org.mockito:mockito-core:jar:1.9.5:test
[INFO] |  \- org.objenesis:objenesis:jar:1.0:test
[INFO] \- org.hamcrest:hamcrest-library:jar:1.3:test


Boot's using Jetty as it's on the classpath. The attempt to exclude it by excluding EmbeddedServletContainerAutoConfiguration.EmbeddedJetty.class doesn't work as EmdeddedJetty isn't an auto-configuration class. EmbeddedServletContainerAutoConfiguration is an auto-configuration class and you could exclude it, but then you'd also lose embedded Tomcat support which I don't think is what you want. The easiest thing to do is to eliminate Jetty from your application's classpath.

The dependency tree output shows that you've still got transitive dependencies on org.eclipse.jetty:jetty-server and org.eclipse.jetty:jetty-webapp, both of which are being pulled in by your direct dependency on Update your pom to exclude them:


Update: if excluding Jetty from the dependencies isn't an option, then you can declare your own TomcatEmbeddedServletContainerFactory bean:

public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
    return new TomcatEmbeddedServletContainerFactory();

This will prevent the auto-configuration of the embedded Jetty server as it's conditional on there being no EmbeddedServletContainerFactory bean in the application context.