Neo4j cypher query display tree-like subgraph starting from one node without specific sequence of relations and nodes (as efficent as possible)

后端 未结 2 1793
面向向阳花
面向向阳花 2021-01-23 04:10

I want to Display a tree-like subgraph showing all paths starting from a single node. Most connections are bidirectional(2 edges at the moment probably change to 1 edge instead

2条回答
  •  夕颜
    夕颜 (楼主)
    2021-01-23 04:45

    For this you should create a custom traversal. There is an API for that in Neo4j, take a look at the documentation : https://neo4j.com/docs/java-reference/current/#tutorial-traversal

    To not traverse twice a node, you should use the NODE_PATH as uniqueness rule (in cypher, it's a RELATIONSHIP_PATH uniqueness to avoid graph cycle).

    As @Tezra and @logisima pointed out the Traversal API is the key. But its not done with uniqueness or anything this complex. This finally printed out the results i was looking for:

    TraversalDescription td = graphDb.traversalDescription()
    .depthFirst()
    .uniqueness(Uniqueness.RELATIONSHIP_GLOBAL)
    .evaluator( new Evaluator()
    {
        @Override
        public Evaluation evaluate( final Path path )
        {
            Node parent=null,grandParent =null;
            Relationship rel1=null,rel2=null;
            int nCount=0,rCount=0;
            
            if(path.length()>=2)
            {
                for(Node node : path.reverseNodes())
                {
                    if(nCount==1)
                        parent = node;
                    if(nCount==2)
                        grandParent=node;
                    if(nCount>2)
                        break;
                    nCount++;
                }
                for(Relationship rel : path.reverseRelationships())
                {
                    if(rCount==0)
                        rel1 = rel;
                    if(rCount==1)
                        rel2=rel;
                    if(rCount>1)
                        break;
                    rCount++;
                }
            }
            if(path.length()<2)
            {
                return Evaluation.INCLUDE_AND_CONTINUE;
            }
            else if (path.length()>=2 
                    && rel1.isType(relType)
                    && rel2.isType(relType)
                    && path.endNode().getProperty("CATEGORY").toString().equals(parent.getProperty("CATEGORY").toString())
                    && path.endNode().getProperty("CATEGORY").toString().equals(grandParent.getProperty("CATEGORY").toString()))
            {
                return Evaluation.EXCLUDE_AND_PRUNE;
            }
            else
            {
                return Evaluation.INCLUDE_AND_CONTINUE;
            }
        }
    });
    try ( Transaction tx = graphDb.beginTx() )
    {
        Traverser traverser = td.traverse(graphDb.getNodeById(id));
        for ( Path path : traverser)
        {
            System.out.println(path.toString());
                    }
        tx.success();
    }
    

    (special thanks to @Tezra bringing me on the right track on time)

    A solution with apoc should also be possible and also avoid the problem of getting the graph back into neo4j.

提交回复
热议问题