Reputation: 129
In Neo4j version 3.5.6, is there a "Cypher" way to navigate through the relationships in a depth-first manner? Let me explain using a simple example:
I have nodes labeled P (Persons) that can be linked together using relationships labeled F (Friend). Each node P has an age attribute (the age of the person). I need to compute, on each P node, the ages attribute that is an array containing the age of the person and the ages of all her friends.
As an example, I am a friend of Alice, who is a friend of Bill and Joe. Here is the code that constructs the graph:
create (jp:P{name:"jp", age:35})
create (alice:P{name:"alice", age:32})
create (bill:P{name:"bill", age:25})
create (joe:P{name:"joe", age:42})
create (alice)-[:F]->(bill)
create (alice)-[:F]->(joe)
create (jp)-[:F]->(alice)
I first checked if (by any chance) the default navigation would be on a depth-first basis. First I initialize the ages array to contain the age of the person:
match (p:P) set p.ages = [p.age] return p
This returns what I expected:
╒═════════════════════════════════════╕
│"p" │
╞═════════════════════════════════════╡
│{"name":"jp","ages":[35],"age":35} │
├─────────────────────────────────────┤
│{"name":"alice","ages":[32],"age":32}│
├─────────────────────────────────────┤
│{"name":"bill","ages":[25],"age":25} │
├─────────────────────────────────────┤
│{"name":"joe","ages":[42],"age":42} │
└─────────────────────────────────────┘
Then I navigate through the relationships and updates the ages arrays:
match (p1)-[:F]->(p2) set p1.ages = p1.ages + p2.ages
Checking at the result I obtained:
╒═══════════════════════════════════════════╕
│"p" │
╞═══════════════════════════════════════════╡
│{"name":"jp","ages":[35,32],"age":35} │
├───────────────────────────────────────────┤
│{"name":"alice","ages":[32,25,42],"age":32}│
├───────────────────────────────────────────┤
│{"name":"bill","ages":[25],"age":25} │
├───────────────────────────────────────────┤
│{"name":"joe","ages":[42],"age":42} │
└───────────────────────────────────────────┘
It appears that alice's ages is correct (her own age and the ages of bill and joe), but not jp's, that only contains jp and alice ages, not joe's and bill's. So the default navigation is not depth-first.
I found a way to making it works, although with the drawback to having to hardcode the maximum depth, and issue one query per depth:
match (p:P) set p.ages=[p.age]
match (p1)-[:F*2..2]->(p2) set p1.ages = p1.ages + p2.ages
match (p1)-[:F*1..1]->(p2) set p1.ages = p1.ages + p2.ages
This produces the following (correct) result:
╒═══════════════════════════════════════════╕
│"p" │
╞═══════════════════════════════════════════╡
│{"name":"jp","ages":[35,42,25,32],"age":35}│
├───────────────────────────────────────────┤
│{"name":"alice","ages":[32,42,25],"age":32}│
├───────────────────────────────────────────┤
│{"name":"bill","ages":[25],"age":25} │
├───────────────────────────────────────────┤
│{"name":"joe","ages":[42],"age":42} │
└───────────────────────────────────────────┘
Is there any way, using plain Cypher queries (no Java code), to obtain the same result, without having to hardcoding the maximum depth?
Thanks for your help!
Upvotes: 0
Views: 96
Reputation: 129
I just found a simple answer to that question: use the collect construct:
match (a:P)-[:F*0..10]->(b:P) return a.name, collect(b.age) as ages
This correctly returns:
╒════════╤═════════════╕
│"a.name"│"ages" │
╞════════╪═════════════╡
│"jp" │[35,32,25,42]│
├────────┼─────────────┤
│"alice" │[32,25,42] │
├────────┼─────────────┤
│"bill" │[25] │
├────────┼─────────────┤
│"joe" │[42] │
└────────┴─────────────┘
The only hardcoded info above is the maximum depth (10)
Upvotes: 1