I'm writing Spring Data Neo4j webapp. I have this domain object:
@Node("Person")
public class Person {
@Id
@GeneratedValue
private String id;
and a (:Image) - [:DEPICTS] -> (:Person) relationship.
I need to count images depicting specific person. This was my attempt:
public interface ImageRepositoryNeo4j extends Neo4jRepository<Image, String> {
@Query("""
MATCH (i:Image) -[:DEPICTS] -> (p:Person)
WHERE p = $person
RETURN count(i)
""")
// DSNT WORK
public Integer countByPeopleContains(@Param("person") Person person);
but it always returns 0.
Other failed attempts
@Query("""
MATCH (i:Image)-[:DEPICTS]->(p:Person)
WHERE p IN $people
RETURN count(i)
""")
// DOESN'T WORK, 0 RESULTS
public Integer countByPeopleContains(@Param("people") Collection<Person> people);
@Query("""
MATCH (i:Image) -[:DEPICTS] -> (p:Person)
WHERE ID(p) = $person.__id__
RETURN count(i)
""")
// DSNT WORK
public Integer countByPeopleContains(@Param("person") Person person);
@Query("""
MATCH (i:Image) -[:DEPICTS] -> (p:Person)
WHERE id(p)=$person
RETURN count(i)
""")
// DSNT WORK
public Integer countByPeopleContains(@Param("person") String personId);
@Query("""
MATCH (i:Image) -[:DEPICTS] -> (p:Person {id: $person.__id__})
RETURN count(i)
""")
// DSNT WORK, ALWAYS 0
public Integer countByPeopleContains(@Param("person") Person person);
All of them return 0.
And this one works, but since fullName isn't unique property, it is not suitable by domain logic:
@Query("""
MATCH (i:Image) -[:DEPICTS] -> (p:Person {fullName: $person})
RETURN count(i)
""")
// TODO: personFullname is not unique, use smth else
public Integer countByPeopleContains(@Param("person") String personFullName);
How do I use domain object in custom Cypher query in Spring Data Neo4j Repository?
Clue
In Spring Data Neo4j documentation closest what I've found to what I need is this:
@Node
public final class Movie {
@Id
private final String title;
...
interface MovieRepository extends Neo4jRepository<Movie, String> {
@Query("MATCH (m:Movie {title: $movie.__id__})\n"
+ "MATCH (m) <- [r:DIRECTED|REVIEWED|ACTED_IN] - (p:Person)\n"
+ "return m, collect(r), collect(p)")
Movie findByMovie(@Param("movie") Movie movie);
}
but for some reason it doesn't work in my case. Maybe that's because my id is @GeneratedValue.
Another clue
In Neo4j embedded docs there is an example on how to pass an object to Cypher without Spring Data Neo4j:
Map<String,Object> params = new HashMap<>();
params.put( "node", bobNode );
String query =
"MATCH (n:Person)" + "\n" +
"WHERE n = $node" + "\n" +
"RETURN n.name";
Result result = transaction.execute( query, params );
It works wif $node is org.neo4j.graphdb.Node:
@Bean
CommandLineRunner countForGregWithoutSDN(GraphDatabaseService graphDatabaseService) {
return args -> {
try (var tx = graphDatabaseService.beginTx()) {
String cypherGetGreg = "MATCH (n:Person {birthday: date('1993-03-03')}) RETURN n LIMIT 5;";
Result result = tx.execute(cypherGetGreg);
var greg1993 = (Node) result.next().get("n");
if (!greg1993.getProperty("name").equals("Greg"))
throw new RuntimeException("Should be Greg1993 but actually "+greg1993);
result.close();
Map<String,Object> params = new HashMap<>();
params.put( "node", greg1993 );
String cypherCountForGreg = """
MATCH (i:Image) -[:DEPICTS] -> (p:Person)
WHERE p = $node
RETURN count(i)
""";
result = tx.execute( cypherCountForGreg, params );
var count = result.next().values().iterator().next();
System.out.println("Count for Greg1993 is "+count); // 1
result.close();
}
};
}
but it results in 0 if $node is SDN Entity.
I suspect the problem lies in evaluating node equality.
Instead of
WHERE p = $persontryWHERE id(p) = id($person)UPDATE: Based on this post, I could be mistaken: Should Nodes be compared directly or they must be going through ID() first?