Neo4J:具有特定关系类型序列约束的最短路径

时间:2017-03-24 15:08:34

标签: neo4j cypher shortest-path

我需要找到节点之间的最短路径,但是对良好路径中的关系类型有一些限制。

我有两种关系类型:A& B. 如果路径具有两个或更多类型B的连续关系,则该路径被认为是错误的:

良好路径:() - A->() - A->()< -A - () - B->() - A->() - B->()
错误路径:() - A->() - A->()< -A - () -B->()< -B - () - A- >()

Cypher查询:

MATCH path=allShortestPaths( (p:P{idp:123})-[rel:A|B*]-(p2:P{idp:124}) )
WHERE *some-predicate-on-path-or-rel*
RETURN path

不是解决方案,因为最短的好路径可能比最短的坏路径长。

Q1:一些Cypher查询可以解决这个问题吗?

我可以使用嵌入式Java Neo4J API解决我的问题:

GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase("db/store/dir/path");
TraversalDescription td = graphDb.traversalDescription()
    .breadthFirst()
    .evaluator(Evaluators.toDepth(max_depth))
    .evaluator(Evaluators.endNodeIs(Evaluation.INCLUDE_AND_PRUNE, Evaluation.EXCLUDE_AND_CONTINUE, endNode))
    .evaluator(new DoubleB_PruneEvaluator());

static class DoubleB_PruneEvaluator implements Evaluator {
    @Override
    public Evaluation evaluate(final Path path) {
        Iterator<Relationship> lRels = path.reverseRelationships().iterator();
        if (lRels.hasNext()  &&  lRels.next().isType(MyRelTypes.B)) {
            if (lRels.hasNext()  &&  lRels.next().isType(MyRelTypes.B))
                return Evaluation.EXCLUDE_AND_PRUNE;
        }
        return Evaluation.INCLUDE_AND_CONTINUE;
    }
}

Q2:这个解决方案效率很高吗?或者如何改进?

但我的应用程序是用PHP编写的,并通过REST协议与Neo4j服务器进行交互。

问题3:如何通过某些REST查询运行此解决方案?

1 个答案:

答案 0 :(得分:1)

没有聪明的人不会回答我。所以我会尝试自己。

A1:标准Cypher查询无法解决此问题。 (我的Neo4j版本3.1.1)

A2:由于以下几个原因,此解决方案效率不高:

  1. 标准函数 shortestPath 是使用more实现的 高效双向 BFS。
  2. 此遍历描述不包含停止条件 找到解决方案。遍历将持续到最大值 深度。
  3. 此外,此解决方案只找到一条路径。不会找到相同长度的其他路径。

    A3:可以通过extending Neo4j将Java编码的解决方案添加到服务器中。 我使用user-defined procedures解决了我的问题:

    我/应用/ RelType.java:

    package my.app;
    
    import org.neo4j.graphdb.*;
    
    public enum RelType implements RelationshipType {
        A, B
    }
    

    我/应用/ DoubleB_PruneEvaluator.java:

    package my.app;
    
    import java.util.*;
    import org.neo4j.graphdb.*;
    import org.neo4j.graphdb.traversal.*;
    
    public class DoubleB_PruneEvaluator implements Evaluator {
        @Override
        public Evaluation evaluate(final Path path) {
            Iterator<Relationship> lRels = path.reverseRelationships().iterator();
            if (lRels.hasNext()  &&  lRels.next().isType(RelType.marry)) {
                if (lRels.hasNext()  &&  lRels.next().isType(RelType.marry))
                    return Evaluation.EXCLUDE_AND_PRUNE;
            }
            return Evaluation.INCLUDE_AND_CONTINUE;
        }
    }
    

    我/应用/ Procedures.java:

    package my.app;
    
    import java.util.stream.Stream;
    
    import org.neo4j.graphdb.*;
    import org.neo4j.procedure.*;
    import org.neo4j.graphdb.traversal.*;
    
    public class Procedures {
        @Context
        public GraphDatabaseService db;
    
        @Procedure
        public Stream<PathHit> shortestWo2B( 
                                    @Name("from") Node fromNode,
                                    @Name("to")   Node toNode,
                                    @Name("maxDepth") long maxDepth)
        {
            TraversalDescription td = db.traversalDescription()
                .breadthFirst()
                .relationships(RelType.A)
                .relationships(RelType.B)
                .evaluator(Evaluators.toDepth((int)maxDepth))
                .evaluator(Evaluators.endNodeIs(Evaluation.INCLUDE_AND_PRUNE, Evaluation.EXCLUDE_AND_CONTINUE, toNode))
                .evaluator(new DoubleB_PruneEvaluator());
    
            return td.traverse(fromNode)
                    .stream()
                    .map( PathHit::new );
        }
    
        public static class PathHit {
            public Path path;
    
            public PathHit(Path path) {
                this.path = path;
            }
        }
    }
    

    Doc:https://neo4j.com/docs/java-reference/3.1/javadocs/index.html?org/neo4j/procedure/Procedure.html

    关于编译和安装插件的几句话:

    作为Java的初学者,我认为Eclipse和Maven的实用程序太重了。我更喜欢使用简单的 javac &amp;

    $ export CLASSPATH=/path/to/neo4j-install-dir/lib/*:.
    $ javac my/app/*.java
    $ jar -cf my-neo4j-plugin.jar my/app/*.class
    $ cp my-neo4j-plugin.jar /path/to/neo4j-install-dir/plugins/
    $ /path/to/neo4j-install-dir/bin/neo4j restart
    

    现在我们可以运行Cypher查询:

    MATCH (p1:P{idp:123}) 
    MATCH (p2:P{idp:124})
    CALL my.app.shortestWo2B(p1,p2,100) YIELD path 
    RETURN path;