Reputation: 51
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.
Upvotes: 1
Views: 306
Reputation: 30397
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
Upvotes: 1