【问题标题】:DSE Graph with Java Driver, how to walk through a graph (like a tree)DSE Graph with Java Driver,如何遍历图(如树)
【发布时间】:2016-07-05 17:32:52
【问题描述】:

我有一个子图,我知道如何到达根顶点。但接下来我需要走一遍。

在我的情况下,具体“遍历子图”意味着我必须走到子图的所有叶子(因为我知道子图就像一棵树),然后返回路径并在每个顶点之间进行一些计算.

我的问题是,如何以最高效的方式实现这一目标?

我可以考虑两种解决方案。

首先,我使用大量 session.executeGraph("g.V().has('id','1')").one() 语句遍历图表,以获取所有单个顶点和边并使用它们进行计算。但是我觉得这种方式效率很低。

或者我使用我可以获得的路径对象

GraphNode node = session.executeGraph("g.V().has('id','1').repeat(outE().subgraph('sg').otherV()).cap('sg').path()").one();
Path path = node.asPath();

我很确定,第二种解决方案是首选,但我不知道如何使用路径对象来遍历图形,因为我唯一能看到的是对象的平面图。

更新 #1

这是示例树的图片。目标,我需要节点 A 的“组合值”。规则非常简单。节点(根除外)具有值。边缘具有权重。我必须总结所有关于权重的值。只要孩子只有一个父母,我就可以接受完整的价值。如果一个孩子有多个父母,我必须考虑权重。在示例树中,B 的组合值为 100 + (500 * 50/60) + 1000A 的组合值为 combined value of B plus value of C (A == 2156.67)。所以,我需要顶点和边的属性来进行计算。

更新 #2

所以,这是我的解决方案。

我已经实现了一个抽象的 Tree 类来进行实际的计算(因为我也有一个模拟实现)。

public abstract class Tree {
    // String == item id
    protected final Map<String, Item> items = new HashMap<>();
    private final String rootItemId;

    protected Tree(String rootItemId) {
        this.rootItemId = rootItemId;
    }

    public void accumulateExpenses() {
        accumulateExpenses(null, null);
    }

    private double accumulateExpenses(String itemId, String parentItemId) {
        final Item item = itemId == null ? items.get(rootItemId) : items.get(itemId);
        final double expense = item.getExpense();
        double childExpenses = 0;

        for (String childId : item.getChildIds()) {
            childExpenses += accumulateExpenses(childId, item.getId());
        }

        // calculate the percentage in case the item has multiple parents
        final double percentage = item.getPercentage(parentItemId);
        final double accumulatedExpenses = percentage * (expense + childExpenses);
        item.setAccumulatedExpense(accumulatedExpenses);

        return accumulatedExpenses;
    }
}

并且我已经实现了一个 GraphTree 类,它负责填充超类(抽象树)的项目图。

public class GraphTree extends Tree {
    public GraphTree(GraphNode graphNode, String rootNodeId) {
        super(rootNodeId);

        final GraphNode vertices = graphNode.get("vertices");
        final GraphNode edges = graphNode.get("edges");

        for (int i = 0; i < vertices.size(); i++) {
            final Vertex vertex = vertices.get(i).asVertex();
            final Item item = Item.fromVertex(vertex);
            super.items.put(item.getId(), item);
        }

        for (int i = 0; i < edges.size(); i++) {
            final Edge edge = edges.get(i).asEdge();
            final Relation relation = Relation.fromEdge(edge);
            super.items.get(relation.getParentId()).getRelations().add(relation);
        }
    }
}

为了完整起见,这里也是Item类。

public class Item {
    private String id;
    private double accumulatedExpense;
    private final List<Relation> relations = new ArrayList<>();
    private final Map<String, Expense> expenses = new HashMap<>();

    public void setAccumulatedExpense(double accumulatedExpense) {
        this.accumulatedExpense = accumulatedExpense;
    }

    public double getPercentage(String parentId) {
        if (parentId == null) {
            return 1;
        }

        double totalWeight = 1;
        double weight = 1;

        for (Relation relation : relations) {
            if (Objects.equals(id, relation.getChildId())) {
                totalWeight += relation.getWeight();
                if (Objects.equals(parentId, relation.getParentId())) {
                    weight = relation.getWeight();
                }
            }
        }

        return weight / totalWeight;
    }

    public static Item fromVertex(Vertex vertex) {
        final Item item = new Item();
        item.setId(IdGenerator.generate(vertex));

        return item;
    }

    public List<String> getChildIds() {
        return relations.parallelStream()
                   .filter(relation -> Objects.equals(relation.getParentId(),id))
                   .map(Relation::getChildId)
                   .collect(Collectors.toList());
    }
}

为了获得初始子图,我使用了以下代码。

    final String statement = String.format("g.V('%s').repeat(outE().subgraph('sg').otherV()).cap('sg')", rootNodeId);
    final GraphNode node = session.executeGraph(statement).one();

【问题讨论】:

  • 您是否考虑过进行广度优先搜索? This 展示了一种非常漂亮的方法。将 gremlin 查询传递到 DSE 图中很简单。
  • 谢谢@Fido。但我遇到的问题不是如何获取图形(第二个查询可以正常工作),而是如何与 Java 驱动程序一起使用。因为我得到的是一个GraphNode,它有一个包含所有顶点和边的平面Map。所以我失去了顶点之间的所有重要关系。我所期望的是node.getOutEdges().forEach(edge -&gt; edge.getInVertices()) 之类的东西。所以我的主要问题是如何正确使用 Java 驱动程序。我对 gremlin 很陌生,但我想我到目前为止理解它,但我无法按预期将它转移到 Java。
  • 您要寻找的最终结果是什么?听起来您甚至不需要路径,而只需要您将要积累的某些属性。
  • @DanielKuppitz:我已经更新了我的问题。我希望现在更清楚我必须完成的工作。但也许你是对的,我可以用一个复杂的 gremlin 语句来实现。在那种情况下,我必须了解更多关于我担心的 gremlin :)
  • 谢谢,更新更清楚了。不过还有一个问题:在结果中,您只期望 B 及其值 1516.67。不包括 C,因为它没有子节点,不包括其他节点,因为它们不是 A 的直接子节点。我的假设是否正确?

标签: java datastax-enterprise gremlin datastax-java-driver


【解决方案1】:

即使一遍又一遍地阅读 cmets,当我尝试使用单个查询找到解决方案时,我仍然对逻辑感到困惑。因此,最好只告诉您如何获得树表示:

g.V().has('id','1').repeat(outE().as("e").inV()).emit(__.not(outE())).tree()

如果您只需要某些信息(例如顶点的value 属性和边的weight 属性),您可以这样做:

g.V().has('id','1').
  repeat(outE().as("e").inV()).emit(__.not(outE())).
  tree().by("value").by("weight")

由于顶点A 似乎没有value 属性,您需要添加coalesce 步骤:

g.V().has('id','1').
  repeat(outE().as("e").inV()).emit(__.not(outE())).
  tree().by(coalesce(values("value"), constant(0))).by("weight")

更新

如果我以后需要再次使用示例图,这里是创建它的代码:

g = TinkerGraph.open().traversal()
g.addV().property(id, "A").as("a").
  addV().property(id, "B").property("value", 100).as("b").
  addV().property(id, "C").property("value", 200).as("c").
  addV().property(id, "D").property("value", 500).as("d").
  addV().property(id, "E").property("value", 1000).as("e").
  addV().property(id, "Z").property("value", 900).as("z").
  addE("link").from("a").to("b").property("weight", 80).
  addE("link").from("a").to("c").property("weight", 20).
  addE("link").from("b").to("d").property("weight", 50).
  addE("link").from("b").to("e").property("weight", 40).
  addE("link").from("z").to("d").property("weight", 10).iterate()

【讨论】:

  • 我很难过,我无法更好地描述我的问题。但是你已经帮助了我,因为我认为我可以(必须?)只用一个或多个棘手的 gremlin 语句来解决问题。所以,我必须深入挖掘gremlin。我将以您的陈述为起点。谢谢!也许再问一个问题对我有帮助......你认为我应该用 gremlin 还是在 Java 中解决这个问题?
  • 您是指 Groovy 还是 Java?由你决定,我个人更喜欢使用 Java。
  • 我的意思是,我应该在 gremlin 中进行 de 计算(包括树遍历)还是应该只阅读子图并在 Java 中进行计算?
  • 哦,是的,这可能要容易得多。一旦你得到正确的结果,也许发布你的解决方案。然后我会再次查看它,看看我们是否可以将其转换为单个 Gremlin 查询。
  • 好的,再次感谢。我将发布代码,只要我实现了解决方案。
猜你喜欢
  • 2016-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多