1
votes

I have done some googling and tried several things myself but I have not yet found a solution. I'm rather new to graph databases, Neo4j and cypher.

Attached is an image which I hope will make it easier to explain. The graph has Block (pink), Input (green), Output(blue) and Property (red) nodes and the relationship types are PART_OF, connectsTo, hasInPort and hasOutPort.

I wish to retrieve all nodes from block_3, going in the direction of the [:connectsTo] relations. So what I expect back are blocks 3, 2, 4, and 5 with all their input, output and property nodes. Block_3's input should also be included.

I think I still lack understanding of quite how the queries work. I would really appreciate it if someone could tell me exactly what the query should look like and then give an explanation of the different parts of the query.

Neo4J database

1

1 Answers

1
votes

The general approach is to find the relationship/direction pattern needed to reach all the desired :Block nodes from your starting :Block node, and once you have all those :Block nodes, match to their :Input, :Output, and :Property nodes.

The biggest complication here is the conflicting relationship directions with respect to the pattern you want to follow. :hasOutPort and :connectsTo are both outgoing, which is good, but the :hasInPort relationship points in the opposite direction.

This is a problem because variable-length relationships using multiple relationship types, which is the ideal tool to solve this traversal, can only specify a single direction for all relationships involved...if we let it be any direction instead (by omitting the arrow on the relationship) then it would match to incoming :connectsTo relationships, which is not what you want.

Option 1: Change relationship type/direction

One easy modeling fix would be to change the relationship between :Input nodes and :Block nodes.

Instead of what you have currently:

(:Block)-[:hasInPort]->(:Input)

You could instead use this:

(:Block)<-[:inPortFor]-(:Input)

If you made this change, then the path you want all goes in the same direction, and the query becomes easy:

// match from starting node to all :Block nodes along desired relationships
MATCH (:Block{name:'block_3'})-[:hasOutPort|:connectsTo|:inPortFor*0..]->(block:Block)
// use pattern comprehension to get lists of :Properties, :Input, and :Output nodes per :Block
WITH block, [(block)-[:PART_OF]->(prop) | prop] as properties, 
 [(block)-[:hasOutPort]->(output) | output] as outputs,
 [(block)<-[:inPortFor]-(input) | input] as inputs
RETURN block, properties, outputs, inputs

If you can't or won't change the relationship direction/type between :Input and :Block nodes, then you can't take this approach (the first variable-length match) and need a different approach.

Option 2: APOC Procedures

APOC Procedures is a plugin for Neo4j with a good number of very useful procedures and functions. One of these is a path expander which allows specification of expansion along different relationships, with the ability to declare which direction to follow for each separate relationship type. Using this, as well as a termination label filter (so you only get matches to :Block nodes), we can replace the first match.

// match from starting node to all :Block nodes along desired relationships
MATCH (start:Block{name:'block_3'})
CALL apoc.path.subgraphNodes(start, {labelFilter:'/Block', 
 relationshipFilter:'hasOutPort>|connectsTo>|<hasInPort'}) YIELD node as block
// use pattern comprehension to get lists of :Properties, :Input, and :Output nodes per :Block
WITH block, [(block)-[:PART_OF]->(prop) | prop] as properties, 
 [(block)-[:hasOutPort]->(output) | output] as outputs,
 [(block)-[:hasInPort]->(input) | input] as inputs
RETURN block, properties, outputs, inputs