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