java - 使用 Java 驱动程序的 DSE 图形,如何遍历图形(如树)

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

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

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

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

我可以想到两种解决方案。

首先,我使用大量 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();

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

更新#1

这是示例树的图片。目标是,我需要节点 A 的“组合值”。规则非常简单。节点(根除外)都有值。边缘有权重。我必须将所有有关权重的值相加。只要 child 只有一个 parent ,我就可以获取完整的值。如果一个 child 有多个 parent ,我必须考虑权重。在示例树中,B 的组合值为 100 + (500 * 50/60) + 1000A 的组合值将是 B 的组合值加上 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 类,它负责填充父类(super class)(抽象树)的项目映射。

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();

最佳答案

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

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()

关于java - 使用 Java 驱动程序的 DSE 图形,如何遍历图形(如树),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38209354/

相关文章:

java - 如何使用 Gremlin 和 Java 查询远程 Apache Tinkerpop 图形数据库?

java - 将自定义配置器与 WebSockets 结合使用

java - 创建自己的 FrameLayout 类不起作用

java - 用于呈现未知行数的 JTable

javascript - 如何将 WebSocketServlet 添加到具有上下文路径的嵌入式 Jetty 服务器?

java - 无法连接到 Cassandra - Cassandra Datastax 驱动程序 3.6 中的 Guava 兼容性问题

java - Cassandra 无法创建 Java 虚拟机

java - 如何从 Cassandra 中的计数器列族获取多行键值。

gremlin - 有没有办法反转 Gremlin 中的列表?

c# - Gremlin,如何获取帖子和所有评论以及评论上的嵌套评论,有点像 reddit。在 C# 中