2

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

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

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

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

显示一个典型的DAG

汽车元素 - 文件和修订

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

截止2017-01-03的汽车状态: 成品车

截止2016-08-10的汽车状态: 汽车的发动机还没有涡轮增压器

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

4

1 回答 1

6

虽然图片很有帮助,但在询问有关 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. 我将您的日期转换为 long 以便于比较。我不确定 CosmosDB 是否可以很好地处理与lte谓词相关的日期,has()但如果确实如此,您可能更愿意走这条路。
  2. repeat()步骤允许在树中进行任意深度遍历,但请注意它之后包含的重复逻辑emit()- 这会在循环结束时抓住最终的“树的叶子”,repeat()因为没有更多outE()的遍历。
  3. 里面的逻辑repeat()看起来有点复杂,但基本上只是说当前“文档”遍历所有“修订”,按日期降序排序并获取第一个。一旦它具有您关心的日期所控制的最新修订版,就可以遍历它所连接的任何其他文档。
  4. 在这种情况下,我使用tree()了 step ,因为 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:[]]]]]
于 2017-07-03T13:00:01.243 回答