Search code examples
neo4jcypher

Grouping Cypher query results by direct relationships


Given the following graph (where all relationships have the FOLLOWS type), how can we get the results such that they are grouped according to previous and next node(s) of a given node?

 H->G->E->D
     ->F
  • Where H follows G
  • Where G follows both E & F.
  • E follows D

So given some property of G, expecting results in the form: [{H}, {G}, {E, F}, {D}]

Tried:

MATCH (prev:Person)<-[:FOLLOWS *0..]-(p:Person)<-[:FOLLOWS *0..]-(next:Person) WHERE p.name ='G' RETURN collect(prev), p, collect(next) as result

Solution

  • From your question, there looks to be two types of solution.

    To show what I mean I'll use this slightly more complicated graph that includes a merging of FOLLOWS downstream from H:

                                    ┌───┐
                              ┌────▶│ J │
                              │     └───┘
    ┌───┐   ┌───┐   ┌───┐   ┌───┐   ┌───┐
    │ H │──▶│ G │──▶│ E │──▶│ D │──▶│ I │
    └───┘   └───┘   └───┘   └───┘   └───┘
              │               ▲
              │               │
              │     ┌───┐   ┌───┐   ┌───┐
              └────▶│ F │──▶│ L │──▶│ K │
                    └───┘   └───┘   └───┘
    

    To group every path from Person with name = 'H' by length, showing the same Person more than once (because there is more than one path from, for example, H to D), use this:

    MATCH p = (:Person {name: 'H'})-[:FOLLOWS*0..]->(e:Person)
    WITH length(p) AS l, collect(e.name) AS people
    RETURN collect(people) AS result
    

    Result:

    [["H"], ["G"], ["E", "F"], ["D", "L"], ["I", "J", "D", "K"], ["I", "J"]]
    

    If instead you want each person to appear once, you can use the shortestPath function like so:

    MATCH p = shortestPath((:Person {name: 'H'})-[:FOLLOWS*0..]->(e:Person))
    WITH length(p) AS len, collect(e.name) AS people
    RETURN collect(people) AS result
    

    Result:

    [["H"], ["G"], ["E", "F"], ["D", "L"], ["K", "I", "J"]]