Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
03-21-2020 09:11 AM
My graph consists Nodes that have CHILD_OF relationships. I'm able to query and generate a tree structure with the following query:
MATCH p = (:Node { name:"Root" })<-[:CHILD_OF *0..]-(c:Node) with *, relationships(p) as i
with REDUCE (path = '1', index IN i | path + '.' + index.index) AS path, c.name as name
ORDER BY path
RETURN { path: path, name: name }
The query returns:
╒════════════════════════════════════╕
│"{ path: path, name: name }" │
╞════════════════════════════════════╡
│{"path":"1","name":"Root"} │
├────────────────────────────────────┤
│{"path":"1.1","name":"Sub Assy 1"} │
├────────────────────────────────────┤
│{"path":"1.1.1","name":"Part 1"} │
├────────────────────────────────────┤
│{"path":"1.1.2","name":"Part 2"} │
├────────────────────────────────────┤
│{"path":"1.2","name":"Sub Assy 2"} │
├────────────────────────────────────┤
│{"path":"1.2.1","name":"Sub Assy 1"}│
├────────────────────────────────────┤
│{"path":"1.2.1.1","name":"Part 1"} │
├────────────────────────────────────┤
│{"path":"1.2.1.2","name":"Part 2"} │
├────────────────────────────────────┤
│{"path":"1.3","name":"Sub Assy 3"} │
└────────────────────────────────────┘
In order to create a new child, I need to determine the number of existing children for the parent node first. When I try to count them I always get a result of one.
MATCH p = (:Node { name:"Root" })<-[:CHILD_OF *0..]-(c:Node) with *, relationships(p) as i
with COUNT ((:Node { ID: c.ID })<-[:CHILD_OF]-(:Node)) as childcount, i, c
with *, REDUCE (path = '1', index IN i | path + '.' + index.index) AS path, c.name as name
ORDER BY path
RETURN { path: path, name: name, childcount: childcount }
Which returns:
╒════════════════════════════════════════════════════╕
│"{ path: path, name: name, childcount: childcount }"│
╞════════════════════════════════════════════════════╡
│{"path":"1","name":"Root","childcount":1} │
├────────────────────────────────────────────────────┤
│{"path":"1.1","name":"Sub Assy 1","childcount":1} │
├────────────────────────────────────────────────────┤
│{"path":"1.1.1","name":"Part 1","childcount":1} │
├────────────────────────────────────────────────────┤
│{"path":"1.1.2","name":"Part 2","childcount":1} │
├────────────────────────────────────────────────────┤
│{"path":"1.2","name":"Sub Assy 2","childcount":1} │
├────────────────────────────────────────────────────┤
│{"path":"1.2.1","name":"Sub Assy 1","childcount":1} │
├────────────────────────────────────────────────────┤
│{"path":"1.2.1.1","name":"Part 1","childcount":1} │
├────────────────────────────────────────────────────┤
│{"path":"1.2.1.2","name":"Part 2","childcount":1} │
├────────────────────────────────────────────────────┤
│{"path":"1.3","name":"Sub Assy 3","childcount":1} │
└────────────────────────────────────────────────────┘
Which is incorrect. "Root" should have 3 children and "Sub Assy 1" should have 2.
If I do the count a count for "Root" and "Sub Assy 1" all by themselves I get the correct results.
MATCH (:Node { name:"Root" })<-[i:CHILD_OF]-(c:Node) return count(i)
Correctly returns 3
MATCH (:Node { name:"Sub Assy 1" })<-[i:CHILD_OF]-(c:Node) return count(i)
Correctly returns 2
How do I get the correct childcount in my recursive query?
Solved! Go to Solution.
03-22-2020 03:39 AM
count()
is an aggregation function that works across rows. In your case, you don't really need aggregations. You have the reference to the node of interest, c
, already, so what you need is the degree of relationships on c
, and we can get that using the size()
function:
size((c)<-[:CHILD_OF]-()) as childcount
MATCH p = (:Node { name:"Root" })<-[:CHILD_OF *0..]-(c:Node)
WITH reduce (path = '1', rel IN relationships(p) | path + '.' + rel.index) AS path, size((c)<-[:CHILD_OF]-()) as childcount, c.name as name
ORDER BY path
RETURN { path: path, name: name, childcount:childcount}
A couple other things that can help...
We can use map projection to simplify your return, and use apoc.text.join()
from APOC procedures in place of the reduce() function:
MATCH p = (:Node { name:"Root" })<-[:CHILD_OF *0..]-(c:Node)
WITH c, apoc.text.join("1" + [rel in relationships(p) | rel.index], '.') as path, size((c)<-[:CHILD_OF]-()) as childcount
ORDER BY path
RETURN c { .name, path, childcount}
03-21-2020 11:06 AM
Although it is not a good solution but try
Match (c:Node)-[rel:CHILD_OF]->(d:Node)
With d.name as name , size(collect(rel)) as childCount
Return name, childCount
union
Match (c:Node)-[rel:CHILD_OF]->(d:Node)
With c.name as name , size(collect(rel)) as childCount
Return name, childCount
03-21-2020 01:40 PM
Thanks Vivek. On its own that indeed returns the number of children for each parent. How does that fit into my recursive query though?
╒════════════╤════════════╕
│"name" │"childCount"│
╞════════════╪════════════╡
│"Root" │3 │
├────────────┼────────────┤
│"Sub Assy 1"│2 │
├────────────┼────────────┤
│"Sub Assy 2"│1 │
├────────────┼────────────┤
│"Sub Assy 3"│1 │
├────────────┼────────────┤
│"Part 1" │1 │
├────────────┼────────────┤
│"Part 2" │1 │
└────────────┴────────────┘
03-22-2020 03:39 AM
count()
is an aggregation function that works across rows. In your case, you don't really need aggregations. You have the reference to the node of interest, c
, already, so what you need is the degree of relationships on c
, and we can get that using the size()
function:
size((c)<-[:CHILD_OF]-()) as childcount
MATCH p = (:Node { name:"Root" })<-[:CHILD_OF *0..]-(c:Node)
WITH reduce (path = '1', rel IN relationships(p) | path + '.' + rel.index) AS path, size((c)<-[:CHILD_OF]-()) as childcount, c.name as name
ORDER BY path
RETURN { path: path, name: name, childcount:childcount}
A couple other things that can help...
We can use map projection to simplify your return, and use apoc.text.join()
from APOC procedures in place of the reduce() function:
MATCH p = (:Node { name:"Root" })<-[:CHILD_OF *0..]-(c:Node)
WITH c, apoc.text.join("1" + [rel in relationships(p) | rel.index], '.') as path, size((c)<-[:CHILD_OF]-()) as childcount
ORDER BY path
RETURN c { .name, path, childcount}
03-22-2020 07:49 AM
Thank you so much Andrew, you are brilliant! I've been struggling with this for a week.
I have no doubt that if I live long enough, I could have eventually found all of this information in the docs. The challenge I have, and I'm sure others struggle with too, is where to start looking when trying to solve a problem like this. Can you offer any suggestions?
03-22-2020 05:37 PM
Here's a link to one of our important knowledge base articles on understanding Cypher cardinality. I would normally link you to the rest of our Cypher knowledge base articles, but we have a little hiccup there that needs fixing first.
I find it often helps when seeing unexpected results or behavior to go through the query and return at various places to check if the results at that point (table or text results) make sense, and to make sure you understand what rows exist at that point, since further operations will execute per row.
You may also want to review the documentation (and make sure the documentation version matches your Neo4j version) for info on how various functions work.
03-22-2020 05:49 PM
More great advice. Many thanks. Stay healthy,
03-29-2020 06:40 PM
Now I want to return the child node with my query.
type Node {
ID: ID!
name: String
child: [Child]
}
type Child @relation(name: "CHILD_OF") {
from: Node #child
to: Node #parent
index: String
}
type Tree {
path: String
name: String
indent: Int
childCount: Int
node:[Node] // return the Node object
}
type Query {
tree(root: String):[Tree]
@cypher(statement:
"""MATCH p = (:Node { name: $root })<-[:CHILD_OF *0..]-(c:Node)
WITH c, apoc.text.join('1' + [rel in relationships(p) | rel.index], '.') as path, size((c)<-[:CHILD_OF]-()) as childCount, c as Node
ORDER BY path
RETURN c { .name, path, childCount, indent: size(split(path,'.'))-1, Node}"""
)
}
Again I get a nice result in the the neo4J browser but not so much in the playground.
query
{tree(root: "Root"){
path
indent
childCount
node{
name
}
}
}
{
"errors": [
{
"message": "Cannot read property '0' of undefined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"tree"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read property '0' of undefined",
" at buildCypherSelection (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/selections.js:315:20)",
" at recurse (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/selections.js:87:33)",
" at buildCypherSelection (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/selections.js:173:12)",
" at recurse (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/selections.js:87:33)",
" at buildCypherSelection (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/selections.js:173:12)",
" at recurse (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/selections.js:87:33)",
" at buildCypherSelection (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/selections.js:173:12)",
" at customQuery (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/translate.js:558:68)",
" at translateQuery (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/translate.js:501:12)",
" at cypherQuery (/home/rchevalier/dev/grand-stack/api/node_modules/neo4j-graphql-js/dist/index.js:141:40)"
]
}
}
}
],
"data": {
"tree": null
}
}
What am I missing here?
All the sessions of the conference are now available online