Search code examples
neo4jcypher

Neo4j search for nodes while combining relationship types in different directions?


I have a starting node, and I'm supposed to find all matching nodes that can be found using 3 different relationship types.

For example: (n1:NodeType1)-[:Use|Realisation|Aggregation]-(n2:NodeType2)

How would I write a query that would search for n2 using the Use and Realisation relationships in exclusively one direction, and Aggregation exclusively in the other? It can be any combination of the three, within 20 hops.

It should be able to cover all combinations. Examples:

(n1)<-[:Use]-()-[:Aggregation]->()<-[:Use]-()<-[:Realisation]-()-[:Aggregation]->(n2)

(n1)-[:Aggregation]->()-[:Aggregation]->()<-[:Use]-(n2)

I currently have this written:

MATCH (n1:`NodeType1`)<-[:`Use`|`Realisation`*0..20]-()-[`Aggregation`*0..20]->(n2:`NodeType2`)

With this I have a problem that after using the 'Aggregation' relationship, it doesn't check for the other two anymore. Another problem is that this could theoretically reach 40 hops, even though I want it limited to a maximum of 20.

Any help in how I could achieve this?


Solution

  • NOTE: Since your maximum path length is 20, any query could take a very long time or run out of memory, depending on your data characteristics.

    Approach 1

    This query finds every path of up to length 20 using those 3 relationship types in any direction, and then filters for those in which every type points in the required direction (Aggregation must point right, and the others must point left).

    MATCH p=(:NodeType1)-[:Use|Realisation|Aggregation*..20]-(:NodeType2)
    WHERE ALL(i IN RANGE(0, LENGTH(p)-1) WHERE NODES(p)[i] = CASE TYPE(RELATIONSHIPS(p)[i])
      WHEN 'Aggregation' THEN
        STARTNODE(RELATIONSHIPS(p)[i])
      ELSE
        ENDNODE(RELATIONSHIPS(p)[i]) END
    )
    RETURN p
    

    Approach 2

    This may be faster than approach 1.

    This query uses the APOC procedure apoc.path.expandConfig, which has special support for use cases like yours.

    MATCH (n:NodeType1)
    CALL apoc.path.expandConfig(n, {
        relationshipFilter: "<Use|<Realisation|Aggregation>",
        labelFilter: ">NodeType2",
        minLevel: 1,
        maxLevel: 20
    }) YIELD path
    RETURN path