jss
jss

Reputation: 51

Neo4J, cypher: Unidirectional query from specified node

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

Upvotes: 1

Views: 306

Answers (1)

InverseFalcon
InverseFalcon

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

Related Questions