Custom queries don't work with interface projections in Spring Data Neo4j repositories

Viewed 584

To exclude some of the entity's fields I created an interface projection which returns only some of the fields.

I wanted to add a method in the repository interface (which extends Neo4jRepository) which uses a custom query written in Cypher (using the @Query annotation)

This method works if I set the return object to Entity but it returns null when the return object is set to the projection. Projections work with the normal repository methods (without custom queries)

Example code: Also - I use lombok but I doubt that it makes any difference here

PersonEntity.java

@NodeEntity
@Data
public class Person {
  @Id
  @GeneratedValue
  private Long id;
  @Property("first_name")
  private String firstName;
  @Property("last_name")
  private String lastName;
  @Property("is_man")
  private boolean isMan;

PersonProjection.java

public interface PersonProjection {
  Long getId();
  String getFirstName();
  boolean getIsMan();
}

PersonRepository.java

public interface PersonRepository extends Neo4jRepository<Person, Long> {
  @Query("MATCH (n:`Person`) WHERE n.`is_man` = true WITH n RETURN n")
  List<PersonProjection> findMen(); // doesn't work, returns null

  List<PersonProjection> findAllByIsManTrue(); // works, returns the list of men
}

Some things to note:

  1. This cypher query is 100% correct. Tested it in Neo4j Desktop and it works as expected
  2. Of course this example is trivial and I normally wouldn't need a custom query for that but the problem will get back to me when some more complex queries arise
  3. As I said, the custom query method works when I use entity instead of projection. And while using projection, the debug log says looking for concrete class to resolve label: Person so maybe it somehow forces the usage of entity? Just a thing I thought about
  4. I'm using the latest version of spring-boot-starter-data-neo4j

Is there a way to fix it? How could I do it? And if the fix is not possible, what's the other way I could approach this issue?

1 Answers
  1. You need @QueryResult on your interface
  2. The query you use will not work with SDN(spring data neo4j), it needs slight modification while returning the result as below @Query("MATCH (n:Person) WHERE n.is_man = true WITH n RETURN ID(n) as id,n.first_name as firstName, n.is_man as isMan")

Then it will work. Below is the example that works fine

 @QueryResult
public interface PersonProjection {
    String getName();
}
  public interface PersonRepository extends Neo4jRepository<Person, Long> {

   
    @Query("MATCH (pr:Person) return pr.name as name")
    public List<PersonProjection> getPersonWithAllFriends();

   

}
Related