【问题标题】:The Gremlin coalesce step is inconsistent (Cosmos DB / in general?)Gremlin 合并步骤不一致(Cosmos DB / 一般?)
【发布时间】:2019-10-06 01:23:39
【问题描述】:

合并不能作为遍历的第一步,或者导致合并步骤的遍历没有产生至少一个结果。在你忽略这个问题之前,请听我说完。

如果我的图形数据库中有一个 label = 'foo' 和 id = 'bar' 的顶点,并且我想添加一个 label = 'baz' 和 id = 'caz' 的顶点,下面的 Gremlin 查询效果很好。

g.V('bar').coalesce(__.V('caz'), __.addV('baz').property('id', 'caz'))

如果;但是,我去掉了查询的第一部分,查询失败了。

g.coalesce(__.V('caz'), __.addV('baz').property('id', 'caz'))

同样,如果我按如下方式修改查询,它也会失败。

g.V('caz').coalesce(__.V('caz'), __.addV('baz').property('id', 'caz'))

要使合并工作,它必须具有一个包含一个或多个元素的输入集。我理解为什么当合并步骤中的步骤是 has 和 hasLabel 时,这种方法是有意义的;但是,它对 V 和 addV 没有意义。我猜想,coalesce 的服务器实现对 null 或空输入步骤有一个检查/返回,这会取消对该步骤的处理。

如果这是一般 Gremlin 的错误或改进请求,那么解决这个问题会很棒。如果只是 Cosmos DB 的问题,我将直接与 Microsoft 通话。

在此期间,我正在拼命寻找解决方案,以解决仅在元素不存在时才创建元素的挑战。我知道将折叠/展开与合并一起使用;但是,这会杀死我的遍历上下文,使之前定义的别名(使用 as('xyz'))无法使用。考虑到我们正在编写的查询的复杂性,我们不能失去上下文;在大规模处理数据时,我们也负担不起折叠计算。

非常感谢您对上述任何建议。

热烈的问候, 赛博

【问题讨论】:

    标签: gremlin


    【解决方案1】:

    您无法使用 Gremlin 语言中的任何步骤开始遍历。有特定的启动步骤会触发遍历,“触发”是指它们将遍历器放置在管道中进行处理。实际上只有几个启动步骤:V()E()inject()addV()addE()

    我知道将折叠/展开与合并一起使用;但是,这会杀死我的遍历上下文,使之前定义的别名(使用 as('xyz'))无法使用

    如果可以避免的话,您通常不应过度依赖 as()。许多大量使用as() 的遍历通常可以用其他形式重写。由于您对此没有更多详细信息,因此我无法进一步解决。

    在大规模处理数据时,我们也负担不起折叠计算。

    我无法想象 fold()unfold() 会带来大量成本。在最坏的情况下,它会创建一个包含单个项目的List,在最好的情况下,它会创建一个空列表。您可能需要整理大量其他性能优化,然后才能将类似的东西变成您要关注的根本改进。

    说了这么多,我猜你可以这样做:

    gremlin> g = TinkerGraph.open().traversal()
    ==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
    gremlin> g.inject(1).coalesce(V().has('id','caz'),addV('baz').property('id','caz'))
    ==>v[0]
    gremlin> g.inject(1).coalesce(V().has('id','caz'),addV('baz').property('id','caz'))
    ==>v[0]
    

    您使用inject() 和一个一次性值开始遍历,以便将某些内容放入管道。我认为我自己更喜欢fold()unfold() 方法,因为我相信它更具可读性。我也肯定会验证我使用的图表实际上是在使用coalesce() 中嵌入的中间遍历V() 的索引。我希望所有图表都对这种优化很聪明,但我不能完全肯定地说。从这个意义上说,fold()unfold() 工作得更好,因为它们提供了一种更独立于平台的方式来执行您的查询。

    【讨论】:

      【解决方案2】:

      经过一番挖掘,我意识到问题是特定于 Gremlin 语言的,而不是特定于服务器实现的(例如,不是 Cosmos DB 问题)。因此,我使用了两种“如果不存在则添加”模式。

      对于上下文,我们使用 Gremlin 配方提供者模式,该模式可确保在整个产品中为常见任务维护通用约定。因此,当我有一个元素(边或顶点)要创建时,我将它传递给配方提供者以返回带有 addE/addV 的遍历和生成的属性语义。此问题源于生成支持“如果不存在则添加”模式的配方。

      为了解决这个问题,我将一个布尔标志传递给配方提供者,告诉提供者是否使用折叠/展开语义。这样,如果添加配方发生在遍历的开始,应用程序使用折叠/展开语义;如果不是在开始,则不折叠/展开。虽然它在很大程度上是一种解决方法,但我们的应用程序使用的大多数添加配方都不会在遍历开始时发生。

      举个例子,假设我有三个使用标签 vTest 和 ID 的顶点 v1-id、v2-id 和 v3-id,Gremlin 配方提供程序生成的 Gremlin 查询将如下所示:

      g.V('v1-id')
        .has('partitionKey','v1')
        .fold()
        .coalesce(
          __.unfold(),
          __.addV('vTest')
            .property('id','v1-id')
            .property('partitionKey','v1')
        ).coalesce(
          __.V('v2-id')
            .has('partitionKey','v2'),
          __.addV('vTest')
            .property('id','v2-id')
            .property('partitionKey','v2')
        ).coalesce(
          __.V('v3-id')
            .has('partitionKey','v3'),
          __.addV('vTest')
            .property('id','v3-id')
            .property('partitionKey','v3')
        )
      

      因为查询的每个部分都保证返回一个结果,所以coalesce() 始终有效。但是,我相信你会同意,给猪涂口红。

      不幸的是,我们应用中的所有用户注册都将受到fold() / unfold() 方法的影响,因为该过程涉及创建第一个顶点。我当然希望将来看到 Gremlin 的更新,以合并或其他处理条件的步骤。

      【讨论】:

      • 为什么要这么复杂?我宁愿看到一个 gremlin 错误报告,也不愿看到这样的解决方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-06
      • 1970-01-01
      • 1970-01-01
      • 2023-02-10
      • 2021-11-06
      相关资源
      最近更新 更多