如何使用Gremlin/Titan/TinkerPop3更新特定边缘属性?

10

目标

我有一个简单的任务要完成:设置特定边缘属性的权重。以此场景为例:

Example Graph Structure

我想要做的是更新weight的值。

附加要求

  • 如果边不存在,则应创建它。
  • 两个节点之间可能只存在最多一种相同类型的边缘(即,Joey和Pizza之间不能存在多个type为“eat”的“votes_for”边缘)。
  • 必须使用Titan的Java API解决此问题(其中包括作为TinkerPop 3的一部分的Gremlin)。

我所知道的

我有以下信息:

  • 标记为“user”的Vertex
  • 边缘标签votes_for
  • 边缘属性type的值(在本例中为“eat”)
  • 标记为“meal”的顶点的name属性的值(在本例中为“pizza”),因此也包括它的Vertex

我的想法

我想我需要做类似以下的事情:

  1. 从Joey顶点开始
  2. 找到所有标记为votes_for,具有type为“eat”和一个标记为“meal”的出边,该出边缘具有name为“pizza”的值。
  3. 更新边缘的weight值。

这是我在代码中所尝试的:

//vertex is Joey in this case
g.V(vertex.id())
    .outE("votes_for")
    .has("type", "eat")
    //... how do I filter by .outV so that I can check for "pizza"?
    .property(Cardinality.single, "weight", 0.99);
    //... what do I do when the edge doesn't exist?

正如在代码中所评论的,仍然存在问题。明确指定Titan模式是否有帮助?我不知道是否有任何辅助/实用方法?是否更合理拥有多个vote_for标签而非一个标签+type属性,例如vote_for_eat

感谢您的任何帮助!

2个回答

14

你正走在正确的道路上。查看顶点步骤文档

标记边,然后从边遍历到顶点进行检查,完成后跳回到边来更新属性。

g.V(vertex.id()).
  outE("votes_for").has("type", "eat").as("e").
  inV().has("name", "pizza").
  select("e").property("weight", 0.99d).
  iterate()

完整的 Gremlin 控制台会话:

gremlin> Titan.version()
==>1.0.0
gremlin> Gremlin.version()
==>3.0.1-incubating
gremlin> graph = TitanFactory.open('inmemory'); g = graph.traversal()
==>graphtraversalsource[standardtitangraph[inmemory:[127.0.0.1]], standard]
gremlin> vertex = graph.addVertex(T.label, 'user', 'given_name', 'Joey', 'family_name', 'Tribbiani')
==>v[4200]
gremlin> pizza = graph.addVertex(T.label, 'meal', 'name', 'pizza')
==>v[4104]
gremlin> votes = vertex.addEdge('votes_for', pizza, 'type', 'eat', 'weight', 0.8d)
==>e[1zh-38o-4r9-360][4200-votes_for->4104]
gremlin> g.E(votes).valueMap(true)
==>[label:votes_for, weight:0.8, id:2rx-38o-4r9-360, type:eat]
gremlin> g.V(vertex.id()).outE('votes_for').has('type','eat').as('e').inV().has('name','pizza').select('e').property('weight', 0.99d).iterate(); g.E(votes).valueMap(true)
==>[label:votes_for, weight:0.99, id:2rx-38o-4r9-360, type:eat]

明确指定Titan模式是否有帮助?

如果您想从Joey节点开始,而没有参考vertex或其id,那么这将是Titan复合索引的一个很好的用例。遍历将从以下位置开始:

g.V().has("given_name", "Joey")

有没有我不知道的帮助/实用方法?

除了TinkerPop参考文档外,还有几个教程可以阅读:

  1. 入门指南
  2. Gremlin控制台
  3. 教程集锦

与一种标签+类型属性(如vote_for_eat)相比,是否更合理使用多个vote_for标签?

这取决于您的图形模型或查询模式,但类似vote_for_eat更细粒度的标签也可以起到作用。您可以在遍历步骤中传递多个边缘标签:

g.V(vertex.id()).outE('vote_for_eat', 'vote_for_play', 'vote_for_sleep')

更新

两个节点之间可能只存在至多一条相同类型的边

您可以使用Titan模式来帮助解决此问题,具体来说是定义一个具有多重性的边标签ONE2ONE。如果在Joeypizza之间创建了超过一个votes_for_eat,则会引发异常。


6

Jason已经回答了你几乎所有的问题,唯一缺少的是:

如果边不存在,则应创建它。

因此,我将尝试用稍微不同的查询来回答这个问题。如果边不存在,此查询将添加一个新边,然后更新/添加 weight 属性:

g.V(vertex.id()).outE('votes_for').has('type', 'eat')
    .where(__.inV().hasLabel('meal').has('name','pizza')) // filter for the edge to update
    .tryNext()                                            // select the edge if it exists
    .orElseGet({g.V(vertex.id()).next()
        .addEdge('votes_for', g.V(pizzaId).next(), 'type', 'eat')}) // otherwise, add the edge
    .property('weight', 0.99)               // finally, update / add the 'weight' property

太棒了,+1。这个优雅的查询满足了所有要求。谢谢! - Double M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接