cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Can't get edges from Neo4jRepository

9mikev
Node Link

I have created a node class called MyNode and an edge class called MyEdge as follows:


@Node
public class MyNode {

    @Id
    String id;
    String name;
    String type;

    @Relationship(type = "CONTAINS")
    Set<MyEdge> outgoingEdges = new HashSet<>();

    public MyNode() {
    }

    public MyNode(String id, String name, String type) {
        this.id = id;
        this.name = name;
        this.type = type;
    }

    // Getters and setters... 

and

@RelationshipProperties
public class MyEdge {

    @Id
    String id;

    String idNodeFrom;
    String idNodeTo;

    @TargetNode
    MyNode targetNode;

    public MyEdge(String idNodeFrom, String idNodeTo, MyNode targetNode) {
        this.id = idNodeFrom + " -> " + idNodeTo;
        this.idNodeFrom = idNodeFrom;
        this.idNodeTo = idNodeTo;
        this.targetNode = targetNode;
    }
    
    // Getters and setters
}

Note that on MyEdge I have annotated the field id with @Id instead of @RelationshipId as being indicated here. And the reason for this is because somehow IntelliJ can't find the @RelationshipId annotation although I have the following in my pom.xml file

        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-neo4j</artifactId>
		</dependency>

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

So my first question is how can I fix that? And my second and most important one is:

Why does the findAll method of the MyEdge repository (as seen below) always return an empty list?

public interface MyEdgeNeo4jRepository extends Neo4jRepository<MyEdge, String> {

    @Query("MATCH ()-[e]->() RETURN e")
    Collection<MyEdge> customFindAllEdges();
}

I have connected to the database through my browser and have verified that the nodes and the relationships are modeled as they should be. But still, when I call

        Collection<MyEdge> ret = myEdgeNeo4jRepository.findAll();
        for (MyEdge myEdge: ret) {
            System.out.println(myEdge);
        }

nothing gets printed.

Another thing I have tried is to use a class-based projection, namely the following class


public class MyEdge2 {

    String idNodeTo;
    String idNodeFrom;

    public MyEdge2() {
    }

    public MyEdge2(String idNodeTo, String idNodeFrom) {
        this.idNodeTo = idNodeTo;
        this.idNodeFrom = idNodeFrom;
    }
    
    // Getters and setters
}

and then try

    @Query("MATCH ()-[e]->() RETURN e")
    Collection<MyEdge2> customFindAllEdges();

but this approach throws the following exception

org.springframework.data.neo4j.core.mapping.NoRootNodeMappingException: Could not find mappable nodes or relationships inside Record<{e: relationship<5630>}> for org.springframework.data.neo4j.core.mapping.DefaultNeo4jPersistentEntity@504aa259

Any help would be appreciated! Thanks very much in advance!

1 ACCEPTED SOLUTION

First step: Change the repository to be of type <MyNode, String>
If you really want to use a custom cypher statement:

@Query("MATCH (n1:MyNode)-[r]->(n2:MyNode) RETURN n1, collect(r), collect(n2)"
MyNode customFindAllEdges(); // of course the name does not really make a lot of sense now

And then operate on the returned and populated object:

MyNode myNode = repository.customFindAllEdges();
for (MyEdge edge : myNode.outgoingEdges) {
}

If there are multiple MyNodes that could get returned, use a List<MyNode> instead of just MyNode.

View solution in original post

10 REPLIES 10

You cannot query directly for relationships. (ok, you showed that it is technical possible with a custom query )
Spring Data Neo4j does not support relationships as first class entities (since version 6.0). If you want to query for MyEdge, you would have to load MyNode (e.g. via a ... extends Neo4jRepository<MyNode, String> ).

Just a side note: The identifier of a RelationshipProperties class should always be of type Long and annotated with @GeneratedValue. The @RelationshipId annotation is (for now) just a shortcut for those annotations. One reason why you have not seen it in your dependencies might be that it is only available from 6.2.x onward.

Can I try a custom query like this?

@Query("MATCH (n1)-[]->(n2) RETURN n1, n2"
ReturnType??? customFindAllEdges();        

But then what would the return type be?

First step: Change the repository to be of type <MyNode, String>
If you really want to use a custom cypher statement:

@Query("MATCH (n1:MyNode)-[r]->(n2:MyNode) RETURN n1, collect(r), collect(n2)"
MyNode customFindAllEdges(); // of course the name does not really make a lot of sense now

And then operate on the returned and populated object:

MyNode myNode = repository.customFindAllEdges();
for (MyEdge edge : myNode.outgoingEdges) {
}

If there are multiple MyNodes that could get returned, use a List<MyNode> instead of just MyNode.

I tried that and it seems to work! So, thank you very much for that. There is still a weird thing going on though.

If i run the following piece of code

        Set<MyNode> nodes = new HashSet<>();
        Set<MyEdge> edges = new HashSet<>();

        // The method you suggested above
        Collection<MyNode> ret = myNodeNeo4jRepository.customFindAllEdges();

        for (MyNode myNode: ret) {
            nodes.add(myNode);
            for (MyEdge myEdge: myNode.getOutgoingEdges()) {
                edges.add(myEdge);
                nodes.add(myEdge.getTargetNode());
                System.out.println(myEdge.getIdNodeFrom() + " -> " + myEdge.getIdNodeTo());
            }
        }

        System.out.println("edges count");
        System.out.println(edges.size());
        System.out.println("nodes count");
        System.out.println(nodes.size());

it prints edge count 2781 whereas if I connect to the db form my browser and run the following query

MATCH ()-[e]->() RETURN count(e)

it returns 2852.

(The nodes count is correct)

Any idea why that might be happening?

Thanks again

I would assume that there are probably 2852-2781 = 71 relationships not having the CONTAINS type.

Not really. There are only relationships of the CONTAINS type.

This query MATCH ()-[e:CONTAINS]->() RETURN count(e) returns 2852 also

What returns MATCH (:MyNode)-[e:CONTAINS]->(:MyNode) RETURN count(e)?

It also returns 2852

Out of curiosity: What is the outcome if you use repository.findAll() instead of the custom query?
It might take a little bit more time because it will load everything reachable from MyNode.

Another idea based on this line:

Set<MyEdge> edges = new HashSet<>();

You do not define an equals in MyEdge that might produce true for two (physical) different edges?

Yeah I looked into it a bit and it seems that there were some multiple edges between the same nodes and thus the equals method I had overrode would not let them be counted in the set. Thanks a lot for that!

One last thing I wanted to ask. I want to get the sub graph that starts from a given node and the cypher query I have written for that is

MATCH p = (n1 {id: $nodeId})-[*]->(n2) RETURN p

But I'm having trouble with the return type. If I write it like this

    @Query("MATCH p = (n1 {id: \"random_id\"})-[*]->(n2) RETURN p")
    Collection<MyNode> customFindSubGraph();

and then use it like this

        Collection<MyNode> ret = myNodeNeo4jRepository.customFindSubGraph();

        for (MyNode myNode: ret) {
            for (MyEdge myEdge: myNode.getOutgoingEdges()) {
                System.out.println(myEdge.getIdNodeFrom() + " -> " + myEdge.getTargetNode());
            }
        }

then for the target node, the set of its outgoing edges is always empty. For example it prints

id_1-> MyNode{id='id_2', name='random_name_2', type='type_1', outgoingEdges=[]}
id_5-> MyNode{id='id_7', name='random_name_7', type='type_2', outgoingEdges=[]}

What I would expect is that I would get the following nodes in the path inside the outgoingEdges list, but it's always empty. What am I doing wrong?

Again, thanks a lot!