首页 > 解决方案 > 如何使用密码查询限制树中每个子级的节点

问题描述

我正在使用 cypher 和 neo4j 我有一个很大的父子关系数据集

(:Person)-[:PARENT_OF*]-(:Person)

我需要在树的每一级上获得只有 5 个孩子(节点)的家谱

我试过了:

MATCH path = (jon:Person )-[:PARENT_OF*]-(:Person)
WITH collect(path) as paths
CALL apoc.convert.toTree(paths) yield value
RETURN value;

它返回给我整个树结构,我尝试用限制限制节点,但它不能正常工作

标签: neo4jcyphergraph-theory

解决方案


I guess you have to filter out the paths first. My approach would be to make sure that all children in the path are among the first 5 child nodes of the previous parent . I don't have a dataset ready to test it, but it could be along the lines of this

MATCH path = (jon:Person )-[:PARENT_OF*]->(leaf:Person)
// limit the number of paths, by only considering the ones that are not parent of someone else.
WHERE NOT (leaf)-[:PARENT_OF]->(:Person)

// and all nodes in the path (except the first one, the root) be in the first five children of the parent
AND 

ALL(child in nodes(path)[1..] WHERE child IN [(sibling)<-[:PARENT_OF]-(parent)-[:PARENT_OF]->(child) | sibling][..5])

WITH collect(path) as paths
CALL apoc.convert.toTree(paths) yield value
RETURN value

another approach, perhaps faster, would be to first collect all the first five children of descendants of jon

// find all sets of "firstFiveChildren"
MATCH (jon:Person { name:'jon'}),
     (p:Person)-[:PARENT_OF]->(child)
WHERE EXISTS((jon)-[:PARENT_OF*]->(p))
WITH jon,p,COLLECT(child)[..5] AS firstFiveChildren
// create a unique list of the persons that could be part of the tree
WITH jon,apoc.coll.toSet(
 apoc.coll.flatten(
   [jon]+COLLECT(firstFiveChildren)
 )
) AS personsInTree


MATCH path = (jon)-[:PARENT_OF*]->(leaf:Person)
WHERE NOT (leaf)-[:PARENT_OF]->(:Person)
AND ALL(node in nodes(path) WHERE node IN personsInTree)
WITH collect(path) as paths
CALL apoc.convert.toTree(paths) yield value
RETURN value;

UPDATE

The issue with the data is that the tree is not symmetric, e.g. not all the paths have the same depth. node d0 for instance has no children. So if you pick five children at the first level, you may not be getting any deeper.

I added a slightly different approach, that should work with symmetric trees, and which allows you to set the number max number of children per node. Try it with 3, and you will see that you only get nodes from the first level., with 8 you get more.

// find all sets of "firstChildren"
WITH 8 AS numberChildren

MATCH (jon:Person { name:'00'}),
       (p:Person)-[:PARENT_OF]->(child)
WHERE EXISTS((jon)-[:PARENT_OF*0..]->(p))
WITH jon,p,COLLECT(child)[..numberChildren] AS firstChildren

// create a unique list of the persons that could be part of the tree
WITH jon,apoc.coll.toSet(
 apoc.coll.flatten(
   [jon]+COLLECT(firstChildren)
 )
) AS personsInTree



MATCH path = (jon)-[:PARENT_OF*]->(leaf:Person)
WHERE NOT (leaf)-[:PARENT_OF]->(:Person)
AND ALL(node in nodes(path) WHERE node IN personsInTree)

WITH collect(path) as paths
CALL apoc.convert.toTree(paths) yield value
RETURN value

推荐阅读