【问题标题】:How to Perform Once off Side Effect at the Beginning of a Repeat in Gremlin?如何在 Gremlin 的重复开始时执行一次性副作用?
【发布时间】:2021-01-28 11:03:31
【问题描述】:

我需要运行一个 Gremlin 查询,该查询从图的叶子(没有出边的顶点)和无边顶点沿图向下移动,收集起始顶点和传入顶点(一次 1 个级别)直到一定限度。不得超过此限制,因此如果下一级入口顶点会导致计数超过限制,那么我们不会收集这些顶点并返回我们拥有的。这是我目前拥有的:

g.V().or(__.not(outE()),__.not(bothE())).limit(700)
.store('a')
.repeat(__.sideEffect(select('b').store('a')).in().as('b'))
.until(union(cap('a').unfold().count(),select('b').count()).sum().is(gt(700)))
.cap('a').unfold()

问题在于repeat 步骤内的sideEffect 步骤对流中的每个顶点执行一次。无论流中有多少个顶点,我都希望它只执行一次。我该如何做到这一点?

【问题讨论】:

    标签: gremlin azure-cosmosdb-gremlinapi


    【解决方案1】:

    我不确定这种方法是否适用于 CosmosDB,但它很可能是您可以在 Gremlin 中实际采用的唯一方法,它不涉及多个 Gremlin 请求和额外处理。我使用这个示例图进行演示:

    g = TinkerGraph.open().traversal()
    g.addV().property(id,'A').as('a').
               addV().property(id,'B').as('b').
               addV().property(id,'C').as('c').
               addV().property(id,'D').as('d').
               addV().property(id,'E').as('e').
               addV().property(id,'F').as('f').
               addE('next').from('a').to('b').
               addE('next').from('b').to('c').
               addE('next').from('b').to('d').
               addE('next').from('c').to('e').
               addE('next').from('d').to('e').
               addE('next').from('e').to('f').iterate()
    

    该方法涉及使用group(),在每次循环通过repeat() 时,您基本上都会形成一个新的遍历对象分组:

    gremlin> g.V('A').
    ......1>   group('m').by(constant(-1)).
    ......2>   repeat(out().group('m').by(loops())).
    ......3>   cap('m')
    ==>[-1:[v[A]],0:[v[B]],1:[v[C],v[D]],2:[v[E],v[E]],3:[v[F],v[F]]]
    

    这为您提供了要处理的数据结构,现在您只需要确保尽早终止 repeat()

    gremlin> g.V('A').
    ......1>   group('m').by(constant(-1)).
    ......2>   until(cap('m').select(values).unfold().count(local).sum().is(gte(3))).
    ......3>     repeat(out().group('m').by(loops())).
    ......4>   cap('m')
    ==>[-1:[v[A]],0:[v[B]],1:[v[C],v[D]]]
    

    在上面的示例中,我们查看until() 中的“m”,并对目前收集到的所有顶点进行计数。当它超过我们的最大值时,在本例中为“3”,我们退出。当我们退出时,我们可以看到我们可能收集到了比我们需要的更多的东西。在这个例子中我们做了,所以我们需要把它扔掉。从技术上讲,您需要除最后一个分组之外的所有分组来满足您的限制,但不幸的是,使用 Gremlin,“除最后一个分组之外的所有分组”并不容易。我最终采用了这种方法,它基本上抓住了最后一个要扔掉的项目,然后将其用作结果的过滤器。请注意,我们得到两个结果,因为遍历到下一个级别将超过我们的“3”个结果总数的限制:

    gremlin> g.V('A').
    ......1>   group('m').by(constant(-1)).
    ......2>   until(cap('m').select(values).unfold().count(local).sum().is(gt(3))).
    ......3>     repeat(out().group('m').by(loops())).
    ......4>   cap('m').
    ......5>   select(values).as('v').
    ......6>   tail(local).as('e').
    ......7>   select('v').unfold().
    ......8>   where(P.neq('e')).
    ......9>   unfold()
    ==>v[A]
    ==>v[B]
    

    请注意,当我们从“3”限制增加到“4”时,结果会发生变化,因为遍历到下一个级别将在总数中增加 2,但总数不会超过 4。

    gremlin> g.V('A').
    ......1>   group('m').by(constant(-1)).
    ......2>   until(cap('m').select(values).unfold().count(local).sum().is(gt(4))).
    ......3>     repeat(out().group('m').by(loops())).
    ......4>   cap('m').
    ......5>   select(values).as('v').
    ......6>   tail(local).as('e').
    ......7>   select('v').unfold().
    ......8>   where(P.neq('e')).
    ......9>   unfold()
    ==>v[A]
    ==>v[B]
    ==>v[C]
    ==>v[D]
    

    下一个示例指出,我们不考虑重复,因为从您的用例中不清楚预期的内容(或者这是否会起作用),但希望这为您提供了足够的结构,至少可以形成遍历您正在寻找:

    gremlin> g.V('A').
    ......1>   group('m').by(constant(-1)).
    ......2>   until(cap('m').select(values).unfold().count(local).sum().is(gt(5))).
    ......3>     repeat(out().group('m').by(loops())).
    ......4>   cap('m').
    ......5>   select(values).as('v').
    ......6>   tail(local).as('e').
    ......7>   select('v').unfold().
    ......8>   where(P.neq('e')).
    ......9>   unfold()
    ==>v[A]
    ==>v[B]
    ==>v[C]
    ==>v[D]
    

    感谢 Kelvin Lawrence 提出我在此答案中采用的一般方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-30
      • 1970-01-01
      • 2020-01-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多