我有一个子图,我知道如何到达根顶点。但接下来我需要遍历它。
具体来说,在我的例子中,“遍历子图”意味着我必须走到子图的所有叶子(因为我知道子图就像一棵树),然后返回路径并在每个顶点之间进行一些计算.我的问题是,如何以最高效的方式实现这一目标?
我可以想到两种解决方案。
首先,我使用大量 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) + 1000
且 A 的组合值将是 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/