在 Java 中使用 Gremlin 返回唯一路径

Returning unique paths with Gremlin in Java

我遇到了 Gremlin 返回重复路径的问题,我想知道是否有人可以帮助我。我是 Gremlin 的新手,所以如果我犯了任何简单的错误,我深表歉意。

我的数据库的一种简单形式由超过 50 个节点和超过 5000 条边组成,其中边被认为是单向的,但是在两个节点之间可能存在双向的边,从而导致循环结构(因此节点 A -> B - > A),同样你可以有 (A -> B -> C -> A).

我想要完成的是遍历网络,返回起始节点和结束节点之间的每条唯一路径,它没有循环。下面是我的代码示例。

final List<List> paths = new ArrayList<List>();
String endNodeID = endNode.getVertex().getId().toString();
new GremlinPipeline<Vertex, ArrayList<Vertex>>(startNode.getVertex())
    .as("x").out("OutboundLink").simplePath()
    .loop("x", new PipeFunction<LoopPipe.LoopBundle<Vertex>, Boolean>() {
                @Override
                public Boolean compute(final LoopPipe.LoopBundle<Vertex> loopBundle) {
                    return loopBundle.getLoops() <= 8;
                }
            }, new PipeFunction<LoopPipe.LoopBundle<Vertex>, Boolean>() {
                @Override
                public Boolean compute(final LoopPipe.LoopBundle<Vertex> loopBundle) {
                    return endNodeID.equals(loopBundle.getObject().getId().toString());
                }
            }).simplePath().filter(new PipeFunction<Vertex, Boolean>() {

                @Override
                public Boolean compute(final Vertex arg0) {
                    return targetId.equals(arg0.getId().toString());
                }
            }).path(new PipeFunction<Vertex, String>() {
                @Override
                public String compute(final Vertex vertex) {
                    return vertex.getId().toString();
                }
            }).fill(paths);

我相信它的作用

从这里我目前得到以下不需要的行为 - 重复路径 - 到达 endNodeID 但继续的路径。

虽然之后我可以在代码中更正此问题(在一个模拟中,它从 10000 条路径更正为仅 200 条路径),但这看起来确实是一种资源浪费。

如有任何帮助,我们将不胜感激。

对于 Titan 0.5.4,我也从未 simplePath() 为我工作。 API 文档表明它的目的是检测和过滤掉循环路径,但对我来说可能是一种误解。我最终得到的是编写自己的循环检测代码。

示例图:

            +--+        
     +------+V1+------+ 
     |      +--+      | 
     v                | 
              ^       | 
     +--+     |       v 
     |V2+-----+          
     |  |            +--+
     +--+ <----------+V3|
     |               ++-+
     |                | 
     |                | 
     |                | 
     |      +--+      | 
     +----> |V4| <----+ 
            +--+            

以下代码生成路径

  • v1->v2->v4
  • v1->v3->v4
  • v1->v3->v2->v4

Gremlin 管道:

public static List<List<Element>> findPathsBetweenSimple(final Vertex from, final Vertex to) {
    final Object endID = to.getId();
    GremlinPipeline<Vertex, List<Element>> pipe = new GremlinPipeline<>(from);
    pipe.as("startAt")
            .out()
            .loop("startAt", new PipeFunction<LoopBundle<Vertex>, Boolean>() {
                @SuppressWarnings("unchecked")
                @Override
                public Boolean compute(LoopBundle<Vertex> lb) {
                    Boolean doContinue = Boolean.TRUE;
                    LOG.trace("Loop cnt: " + lb.getLoops() + "; Vertex: " + lb.getObject());
                    if (containsLoop(lb.getPath(), lb.getObject())) {
                        LOG.debug("Loop detected in path. Aborting. " + lb.getPath());
                        doContinue = Boolean.FALSE;
                    } else if (endID.equals(lb.getObject().getId())) {
                        LOG.debug("Path found: " + lb.getPath() + ", " + lb.getObject());
                        doContinue = Boolean.FALSE;
                    }
                    return doContinue;
                }
            })
            .has("id", endID)
            .path();
    return pipe.toList();
}

循环检测:

private static boolean containsLoop(final List<Element> path, Vertex current) {
    boolean loopDetected = false;

    final List<Vertex> vPath = new ArrayList<>();
    for (Element element : path) {
        if (element instanceof Vertex) {
            vPath.add((Vertex) element);
        }
    }
    vPath.add(current);

    for (Vertex v : vPath) {
        if (Collections.frequency(vPath, v) > 1) {
            loopDetected = true;
            break;
        }
    }

    return loopDetected;
}