【问题标题】:How to get a subgraph consisting of all vertices, that satisfy specific condition如何获得由满足特定条件的所有顶点组成的子图
【发布时间】:2017-07-03 08:54:27
【问题描述】:

DocumentRevision 是驻留在我们领域逻辑特定层中的两个对象。

Document 代表您能想到的任何材料纸的抽象。也就是说 - 每份合同、发票或图纸都可以称为文档

另一方面,Document 的材料表示是 Revision:建筑工程师在现场收到的文件列表代表 Revision 设计师创建的Document。如果由于错误或更改要求而必须更改图纸中的某些内容,则现场将显示一个新的修订版本 - Revision #2 的同一文档

修订版可以包含指向其他文档的链接;因此,我们可以描述汽车、车门、发动机、车轮等之间的关系,以及每个元素独立进化同时与其他元素保持联系的可能性。

显示一个典型的DAG

我设法使用C# Graph API 将所有顶点和边插入到 CosmosDB 中。 我设法遍历图表并执行简单的查询,以便找出汽车有多少修订版,或者引擎在最初创建时是否有涡轮增压器。 但是,我正在努力编写一个复杂的查询,它只返回每个零件或汽车的最新版本,或者一个返回汽车状态的查询,直到 2016 年 8 月 10 日。

截至2017-01-03的汽车状态:

截至2016-08-10的汽车状态:

当遍历访问顶点的后代(它的“out()”)时,我无法找到一种方法来获取最近创建的并继续遍历而不深入挖掘其他顶点。如果你建议我一个表达式,我将不胜感激,它只返回图片中的彩色顶点。

【问题讨论】:

    标签: azure-cosmosdb gremlin tinkerpop3 gremlin-server


    【解决方案1】:

    虽然图片很有帮助,但在询问有关 Gremlin 的问题时,始终提供可以生成图表样本的 Gremlin 脚本会很有帮助。例如,对于您的问题:

    graph = TinkerGraph.open()
    g = graph.traversal()
    g.addV('car').property('name','car').as('car').
      addV('rev').property('name','car revision 1').property('date', 1470787200L).as('carV1').
      addV('rev').property('name','car revision 2').property('date', 1472688000L).as('carV2').
      addV('frontLeftDoor').property('name','front left door').as('frontLeftDoor').
      addV('frontRightDoor').property('name','front right door').as('frontRightDoor').
      addV('engine').property('name','engine').as('engine').
      addV('turbocharger').property('name','turbocharger').as('turbocharger').
      addV('rev').property('name','front left door revision 1').property('date',1470787200L).as('frontLeftDoorV1').
      addV('rev').property('name','front left door revision 2').property('date',1472688000L).as('frontLeftDoorV2').
      addV('rev').property('name','front right door revision 1').property('date',1470787200L).as('frontRightDoorV1').
      addV('rev').property('name','engine revision 1').property('date',1470787200L).as('engineV1').
      addV('rev').property('name','engine revision 2').property('date',1472688000L).as('engineV2'). 
      addV('rev').property('name','engine revision 3').property('date',1483401600L).as('engineV3').
      addV('rev').property('name','turbocharger revision 1').property('date',1470787200L).as('turbochargerV1'). 
      addV('rev').property('name','turbocharger revision 2').property('date',1472688000L).as('turbochargerV2'). 
      addE('relates').from('car').to('carV1').
      addE('relates').from('car').to('carV2').
      addE('relates').from('carV1').to('frontLeftDoor').
      addE('relates').from('carV1').to('frontRightDoor').
      addE('relates').from('carV1').to('engine').
      addE('relates').from('carV2').to('frontLeftDoor').
      addE('relates').from('carV2').to('frontRightDoor').
      addE('relates').from('carV2').to('engine').
      addE('relates').from('frontLeftDoor').to('frontLeftDoorV1').
      addE('relates').from('frontLeftDoor').to('frontLeftDoorV2').
      addE('relates').from('frontRightDoor').to('frontRightDoorV1').
      addE('relates').from('engine').to('engineV1').
      addE('relates').from('engine').to('engineV2').
      addE('relates').from('engine').to('engineV3').
      addE('relates').from('engineV2').to('turbocharger').
      addE('relates').from('engineV3').to('turbocharger').
      addE('relates').from('turbocharger').to('turbochargerV1').
      addE('relates').from('turbocharger').to('turbochargerV2').iterate()
    

    与开发提供答案的 Gremlin 相比,回答问题的人通常需要更多时间来为问题创建示例图。

    无论如何,这是使用“2016 年 8 月 10 日”作为“开始日期”的一种方法:

    gremlin> g.V().has('name','car').
    ......1>   repeat(local(out().has('date',lte(1470787200L)).
    ......2>                order().
    ......3>                  by('date',decr).limit(1)).
    ......4>          out()).
    ......5>     emit().
    ......6>   local(out().has('date',lte(1470787200L)).
    ......7>         order().
    ......8>           by('date',decr).limit(1)).
    ......9>   tree().by('name')
    ==>[car:[car revision 1:[front right door:[front right door revision 1:[]],engine:[engine revision 1:[]],front left door:[front left door revision 1:[]]]]]
    

    这是相同的遍历,但日期不同 - “2017 年 1 月 1 日”:

    gremlin> g.V().has('name','car').
    ......1>   repeat(local(out().has('date',lte(1483228800L)).
    ......2>                order().
    ......3>                  by('date',decr).limit(1)).
    ......4>          out()).
    ......5>     emit().
    ......6>   local(out().has('date',lte(1483228800L)).
    ......7>         order().
    ......8>           by('date',decr).limit(1)).
    ......9>   tree().by('name')
    ==>[car:[car revision 2:[front right door:[front right door revision 1:[]],engine:[engine revision 2:[turbocharger:[turbocharger revision 2:[]]]],front left door:[front left door revision 2:[]]]]]
    

    在这种情况下,请注意排除“引擎修订版 3”,因为它是“2017 年 1 月 1 日”之后的唯一顶点 - 树的其余部分都存在。

    几点说明:

    1. 为了便于比较,我将您的日期转换为长整数。我不确定 CosmosDB 是否可以很好地处理与 has()lte 谓词相关的日期,但如果是这样,您可能更愿意走这条路。
    2. repeat() 步骤允许在树中进行任意深度遍历,但请注意它在emit() 之后包含的重复逻辑 - 这会在repeat() 中抓取最终的“树的叶子”循环结束,因为没有更多的outE() 可以遍历。
    3. repeat() 中的逻辑看起来有点复杂,但它基本上只是说当前“文档”遍历所有“修订”,按日期降序排序并获取第一个。一旦它具有您关心的日期所控制的最新修订版,请遍历它所连接的任何其他文档。
    4. 在这种情况下,我使用了tree() 步骤,因为 CosmosDB 似乎支持这一点。看起来他们还不支持subgraph()。从技术上讲,Apache TinkerPop C# Gremlin 语言变体甚至不支持该步骤 - 不幸的是,那里存在一些挑战,使 Java 仅具有功能。幸运的是,您的数据形状是树状的,所以tree() 步骤似乎就足够了。

    在 Groovy 中,您可以通过闭包的方式提供重复的逻辑,以使事情更具可重用性:

    gremlin> traverseAndFilter = { out().has('date',lte(1470787200L)).
    ......1>                       order().
    ......2>                         by('date',decr).limit(1) }
    ==>groovysh_evaluate$_run_closure1@1d12e953
    gremlin> g.V().has('name','car').
    ......1>   repeat(local(traverseAndFilter()).out()).
    ......2>     emit().
    ......3>   local(local(traverseAndFilter())).
    ......4>   tree().by('name')
    ==>[car:[car revision 1:[front right door:[front right door revision 1:[]],engine:[engine revision 1:[]],front left door:[front left door revision 1:[]]]]]
    

    或存储“traverseAndFilter”遍历本身和clone()它:

    gremlin> traverseAndFilter = out().has('date',lte(1470787200L)).
    ......1>                       order().
    ......2>                         by('date',decr).limit(1);[] 
    gremlin> g.V().has('name','car').
    ......1>   repeat(local(traverseAndFilter.clone()).out()).
    ......2>     emit().
    ......3>   local(local(traverseAndFilter.clone())).
    ......4>   tree().by('name')
    ==>[car:[car revision 1:[front right door:[front right door revision 1:[]],engine:[engine revision 1:[]],front left door:[front left door revision 1:[]]]]]
    

    【讨论】:

    • 很抱歉没有发布插入脚本!下次我一定会这样做!
    • 查询应该在文档的所有修订中找到截至指定日期的最新修订。它应该是这样的:获取当前文档的所有修订 -> OrderByDate->GetClosestAndNotGreatedThan Date -> 继续使用找到的修订指向的文档。如果有早于 2016-08-10 的修订版,它们也将被 lte(1470787200L) 采用
    • 在上面的例子中,第一次修订的日期是偶然匹配的。绝对有可能每个修订版都有不同的日期,并且提供的日期(解决日期)是随机的。 “树的解析”随时可能发生。
    • 我想我将旧版本的代码复制/粘贴到我的答案中。我什至在我的示例图中有错误的数据。无论如何,我解决了一些我认为可以解决您的问题的问题。我什至使用“2017 年 1 月 1 日”添加了第二次遍历,以表明它不受任何特定日期的约束
    • 感谢您对我的帮助!我真的很感谢你的奉献时间!但是,我觉得我仍然无法澄清查询的期望行为。 2017 年 1 月 1 日的预期结果应为:[Car]、[Car - Revision2]、[FrontLeftDoor - Revision2]、[FrontRightDoor - Revision1]、[Engine - Revision2]、[Turbocharger - Revision2]。必须删除比上次更旧的修订。第二个查询返回早于 2017 年 1 月 1 日创建的所有修订,但我需要的只是其中的最后一个。
    猜你喜欢
    • 1970-01-01
    • 2021-05-02
    • 2019-06-05
    • 2022-08-05
    • 2021-06-09
    • 2018-06-30
    • 2019-11-13
    • 2012-01-22
    • 2020-02-22
    相关资源
    最近更新 更多