How to get sub graph with custom query

I have the following classes for node and edge


@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
}

and I want to find the sub graph starting at a specific node. The query I have is this

MATCH (root {name: "nodeName"})-[*]->(leaf) RETURN *

but I'm not sure what the return type will be in Java. That is,

@Query("MATCH (root {name: $nodeName})-[*]->(leaf) RETURN *")
ReturnType??? customMethod(@Param("nodeName") String nodeName);

There are no back edges or cycles in my directed graph so it will not produce any type of potential conflicts.

Any help would be appreciated. Thank you

Hello @9mikev

the return type can be of many things.

I assume that you have a repository that looks something like this

interface MyNodeRepository extends Neo4jRepository<MyNode, String> {
}

Than a valid return type would be one of the following

  • MyNode (there is always one, when there's none, this will return null)
  • Optional<MyNode> (there my be none or one)
  • Collection<MyNode> (with whatever collection you want, usually List or Set is appropriate)

The query however needs a bit of shape:

MATCH (root {name: $nodeName})-[r*]->(leaf) RETURN root, collect(r), collect(leaf)

Basically making sure you get one record by root node which is explained here: Spring Data Neo4j

However, when all you wanna do is find that root node by a simple, mapped attribute (name), why are you making it so hard on yourself? This is all you need:

interface MyNodeRepository extends Neo4jRepository<MyNode, String> {
    Optional<MyNode> findOneByName(String name);

    List<MyNode> findAllByName(String name);
}

Those methods are called "derived query methods". SDN understands the domain model and the repository is able to derive a query for you by the given method name. No need to write Cypher for that.

Let us know if this answer was helpful.

Hey @michael_simons1 and thank you for your answer!

I am aware of the derived query methods, but the reason I'm not using findOneByName is because it performs very slowly for some reason. The graph I'm working on is relatively simple - around 630 nodes and 2,800 relationships - and I don't know why the execution time of these built in queries is so slow - we're talking about 30 seconds waiting time for some queries - but I have experimented and found that custom queries work much faster.

Now as to the query you suggested, I tried it like this

@Query("MATCH (root {id: $nodeId})-[r*]->(leaf) RETURN root, collect(r), collect(leaf)")
Optional<MyNode> customSubGraphStartingAt(@Param("nodeId") String nodeId);

but it only returns only the root node and not the rest of the nodes it should return. That is, its outgoingEdges set is empty.

Another thing is that there is a deprecation warning that popped up when using this query. It says

    MATCH (root {id: $nodeId})-[contains*]->(tool) RETURN root, collect(contains), collect(tool)
	                          ^
Binding relationships to a list in a variable length pattern is deprecated. (Binding a variable length relationship pattern to a variable ('contains') is deprecated and will be unsupported in a future version. The recommended way is to bind the whole path to a variable, then extract the relationships:
	MATCH p = (...)-[...]-(...)
	WITH *, relationships(p) AS contains)

I considered using the path version of this query but again I'm not sure what the return type in Java should be.

Again, thanks for your time and help!

@gerrit.meier Any thoughts on this...?

I also tried this, both Database-side reduction (which was very slow) and Client-side reduction (was not working and can't really recall the reason, it's been some days now) and I don't know what else to try.

Any help would be appreciated

Please have a look at neo4j-issues-examples/discourse-52793 at master · meistermeier/neo4j-issues-examples · GitHub I created a demo project with your reported problem. Of course with more naive dataset missing some relationships.
I don't know where the difference is but I hope that it can help us to tackle down your problem.
The check for the relationships until the last both nodes is a little bit stupid because it checks every chain over and over, but hey, it shows that all is loaded.
The graph:

Alright, I increased the amount of relationships a bit to match yours.
Please try to apply this version of the query and you should be fine. neo4j-issues-examples/MyNodeRepository.java at 8f46768fc873a00f4ccd1cd519f0d0d17c427313 · meistermeier/neo4j-issues-examples · GitHub

First of all thanks so much for your time helping me with this.

The query you suggested is something I had already tried in my pervious answer when I said I tried the Client-side reduction. But the problem is that the query returns the first node, but its set of outgoindEdges is empty, thus it is the only node I get.

Are you really sure that you are using the same query as I have linked?
MATCH p=... RETURN root, collect(nodes(p)), collect(relationships(p))
With this result all outgoingEdges (and their target's outgoingEdges, ...) are mapped. So it could only be that you have another relationship type and not CONTAINS.
Please take the time to see where my example and your project differs, or please provide a reproducer project.