【问题标题】:gremlin clone a node and its edgesgremlin 克隆一个节点及其边缘
【发布时间】:2018-08-17 17:10:28
【问题描述】:

gremlin 是否提供克隆顶点的能力? v1->v2, v1->v3, v1->v4 我怎样才能简单有效地创建一个新顶点v5,它也有指向v2, v3, v4 的边(与v1's 边指向的位置相同),而不必显式设置它们,而是说类似@ 987654325@.

请注意,我使用的是 AWS Neptune 版本的 gremlin,解决方案必须与之兼容。

【问题讨论】:

    标签: neo4j graph-databases gremlin amazon-neptune


    【解决方案1】:

    clone 步骤不存在(目前),但可以通过单个查询来解决。

    让我们从一些示例数据开始:

    gremlin> g = TinkerFactory.createModern().traversal()
    ==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
    gremlin> g.V(4).valueMap(true)                                   // the vertex to be cloned
    ==>[label:person,name:[josh],age:[32],id:4]
    gremlin> g.V(4).outE().map(union(identity(), valueMap()).fold()) // all out-edges
    ==>[e[10][4-created->5],[weight:1.0]]
    ==>[e[11][4-created->3],[weight:0.4]]
    gremlin> g.V(4).inE().map(union(identity(), valueMap()).fold())  // all in-edges
    ==>[e[8][1-knows->4],[weight:1.0]]
    

    现在克隆顶点的查询乍一看可能有点吓人,但它实际上只是一遍又一遍的相同模式 - 在原始和克隆之间跳转以复制属性:

    g.V(4).as('source').
      addV().
        property(label, select('source').label()).as('clone').
      sideEffect(                                                // copy vertex properties
        select('source').properties().as('p').
        select('clone').
          property(select('p').key(), select('p').value())).
      sideEffect(                                                // copy out-edges
        select('source').outE().as('e').
        select('clone').
        addE(select('e').label()).as('eclone').
          to(select('e').inV()).
        select('e').properties().as('p').                        // copy out-edge properties
        select('eclone').
          property(select('p').key(), select('p').value())).
      sideEffect(                                                // copy in-edges
        select('source').inE().as('e').
        select('clone').
        addE(select('e').label()).as('eclone').
          from(select('e').outV()).
        select('e').properties().as('p').                        // copy in-edge properties
        select('eclone').
          property(select('p').key(), select('p').value()))
    

    实际上它看起来像这样:

    gremlin> g.V(4).as('source').
    ......1>   addV().
    ......2>     property(label, select('source').label()).as('clone').
    ......3>   sideEffect(
    ......4>     select('source').properties().as('p').
    ......5>     select('clone').
    ......6>       property(select('p').key(), select('p').value())).
    ......7>   sideEffect(
    ......8>     select('source').outE().as('e').
    ......9>     select('clone').
    .....10>     addE(select('e').label()).as('eclone').
    .....11>       to(select('e').inV()).
    .....12>     select('e').properties().as('p').
    .....13>     select('eclone').
    .....14>       property(select('p').key(), select('p').value())).
    .....15>   sideEffect(
    .....16>     select('source').inE().as('e').
    .....17>     select('clone').
    .....18>     addE(select('e').label()).as('eclone').
    .....19>       from(select('e').outV()).
    .....20>     select('e').properties().as('p').
    .....21>     select('eclone').
    .....22>       property(select('p').key(), select('p').value()))
    ==>v[13]
    gremlin> g.V(13).valueMap(true)                                   // the cloned vertex
    ==>[label:person,name:[josh],age:[32],id:13]
    gremlin> g.V(13).outE().map(union(identity(), valueMap()).fold()) // all cloned out-edges
    ==>[e[16][13-created->5],[weight:1.0]]
    ==>[e[17][13-created->3],[weight:0.4]]
    gremlin> g.V(13).inE().map(union(identity(), valueMap()).fold())  // all cloned in-edges
    ==>[e[18][1-knows->13],[weight:1.0]]
    

    更新

    分页支持有点棘手。让我把整个事情分成三个步骤。我将使用边 ID 作为排序标准并识别最后处理的边(这在 Neptune 中可能不起作用,但您可以使用唯一的可排序属性)。

    // clone the vertex with its properties
    clone = g.V(4).as('source').
      addV().
        property(label, select('source').label()).as('clone').
      sideEffect(
        select('source').properties().as('p').
        select('clone').
          property(select('p').key(), select('p').value())).next()
    
    // clone out-edges
    pageSize = 1
    lastId = -1
    while (true) {
      t = g.V(4).as('source').
        outE().hasId(gt(lastId)).
        order().by(id).limit(pageSize).as('e').
        group('x').
          by(constant('lastId')).
          by(id()).
        V(clone).
        addE(select('e').label()).as('eclone').
          to(select('e').inV()).
        sideEffect(
          select('e').properties().as('p').
          select('eclone').
            property(select('p').key(), select('p').value())).
        count()
      if (t.next() != pageSize)
        break
      lastId = t.getSideEffects().get('x').get('lastId')
    }
    
    // clone in-edges
    lastId = -1
    while (true) {
      t = g.V(4).as('source').
        inE().hasId(gt(lastId)).
        order().by(id).limit(pageSize).as('e').
        group('x').
          by(constant('lastId')).
          by(id()).
        V(clone).
        addE(select('e').label()).as('eclone').
          from(select('e').inV()).
        sideEffect(
          select('e').properties().as('p').
          select('eclone').
            property(select('p').key(), select('p').value())).
        count()
      if (t.next() != pageSize)
        break
      lastId = t.getSideEffects().get('x').get('lastId')
    }
    

    我不知道 Neptune 是否允许您执行完整的脚本 - 如果不允许,您需要在应用程序代码中执行外部 while 循环。

    【讨论】:

    • 你认为这个比例如何和/或这个查询有什么限制?如果有 1,000,000 个传出/传入边缘,这仍然有效吗?这是否适用于 AWS Neptune API?
    • 我认为这最终取决于您使用的图形数据库。但是,拥有这么多事件边通常被视为糟糕的图模型。
    • AWS Neptune 似乎将此查询限制为仅 999 条边,有没有办法对此查询进行分页,以便我可以为具有大量边的节点运行它?
    • 您需要有保证的排序顺序和边缘上的唯一属性,然后它可能会起作用。今晚晚些时候我会尝试把一些东西放在一起,也许只是使用 1 的页面大小扩展前面的示例。
    • 事件边缘指的是传入和传出边缘。对于分页,您需要边缘上的唯一(可排序)标识符/属性,而不是顶点。我会在几分钟后发布更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 2018-04-02
    相关资源
    最近更新 更多