提问者:小点点

Gremlin遍历树以查找不包含具有指定属性的节点的每个分支的最高级别


我有一棵树,就像一个包含州包含城市的国家。例如,下图有美国包含加利福尼亚和德克萨斯,每个州包含2个城市。

一个或多个城市被标记,这意味着它有一个属性“标记”设置为“true”。在下图中,“旧金山”被标记。

g.addV().property('name','united states').as('united states').
addV().property('name', 'california').as('california').
addV().property('name', 'los angeles').as('los angeles').
addV().property('name', 'san francisco').property('marked', 'true').as('san francisco').
addE('contains').from('united states').to('california').
addE('contains').from('california').to('los angeles').
addE('contains').from('california').to('san francisco').

addV().property('name', 'texas').as('texas').
addV().property('name', 'dallas').as('dallas').
addV().property('name', 'houston').as('houston').
addE('contains').from('united states').to('texas').
addE('contains').from('texas').to('dallas').
addE('contains').from('texas').to('houston')

我想运行一个 Gremlin 查询,返回所有未标记的城市和州。如果一个州没有标记的城市,它应该返回该州。如果一个州标记了城市,它不应该返回该州,而应该返回未标记的城市。

此代码在下面正常工作。它输出德克萨斯州,因为德克萨斯州中没有标记城市,并返回洛杉矶,因为洛杉矶是加利福尼亚州唯一未标记的城市。

g.V().has('name', 'united states').repeat(out()).until(not(
  repeat(out()).until(has('marked','true'))
)).not(has('marked','true')).values('name')
==>texas
==>los angeles

但是,这是最有效的查询吗?这对我来说似乎效率低下,因为我正在遍历树,但是在树的每个节点上,我再次遍历该节点下方的树,这似乎很糟糕。是否有更有效的查询?注意:使用 3 个级别(国家、州和城市)只是一个例子。我的实际用例有超过 3 个级别,每个分支可能有可变数量的级别,因此 repeat(out()) 是必要的。

谢谢!


共1个答案

匿名用户

这种情况并不经常发生,但看起来小妖精并不想轻易解决这个问题。寻找一个解决方案花了我一段时间,所以,不情愿地,我只是要粘贴一些丹尼尔·库皮茨的尝试来解决这个问题(尽管事情越深入,对算法规则产生的问题就越多):

gremlin> g = TinkerGraph.open().traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.addV().property('name','united states').as('united states').
......1> addV().property('name', 'california').as('california').
......2> addV().property('name', 'los angeles').as('los angeles').
......3> addV().property('name', 'san francisco').property('marked', 'true').as('san francisco').
......4> addE('contains').from('united states').to('california').
......5> addE('contains').from('california').to('los angeles').
......6> addE('contains').from('california').to('san francisco').
......7> 
......7> addV().property('name', 'texas').as('texas').
......8> addV().property('name', 'dallas').as('dallas').
......9> addV().property('name', 'houston').as('houston').
.....10> addE('contains').from('united states').to('texas').
.....11> addE('contains').from('texas').to('dallas').
.....12> addE('contains').from('texas').to('houston').
.....13> 
.....13> addV().property('name', 'arizona').as('arizona').
.....14> addV().property('name', 'pima').as('pima').
.....15> addV().property('name', 'maricopa').as('maricopa').
.....16> addV().property('name', 'tucson').property('marked', 'true').as('tucson').
gremlin> g = TinkerGraph.open().traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.addV().property('name','united states').as('united states').
......1> addV().property('name', 'california').as('california').
......2> addV().property('name', 'los angeles').as('los angeles').
......3> addV().property('name', 'san francisco').property('marked', 'true').as('san francisco').
......4> addE('contains').from('united states').to('california').
......5> addE('contains').from('california').to('los angeles').
......6> addE('contains').from('california').to('san francisco').
......7> 
......7> addV().property('name', 'texas').as('texas').
......8> addV().property('name', 'dallas').as('dallas').
......9> addV().property('name', 'houston').as('houston').
.....10> addE('contains').from('united states').to('texas').
.....11> addE('contains').from('texas').to('dallas').
.....12> addE('contains').from('texas').to('houston').
.....13> 
.....13> addV().property('name', 'arizona').as('arizona').
.....14> addV().property('name', 'pima').as('pima').
.....15> addV().property('name', 'maricopa').as('maricopa').
.....16> addV().property('name', 'tucson').property('marked', 'true').as('tucson').
.....17> addV().property('name', 'phoenix').as('phoenix').
.....18> addE('contains').from('united states').to('arizona').
.....19> addE('contains').from('arizona').to('pima').
.....20> addE('contains').from('arizona').to('maricopa').
.....21> addE('contains').from('pima').to('tucson').
.....22> addE('contains').from('maricopa').to('phoenix')
==>e[36][25-contains->30]
gremlin> 
gremlin> solution1 = {
......1>   g.V().has('name', 'united states').
......2>     until(select('result').or().not(outE('contains'))).
......3>       repeat(sack(assign).
......4>              map(out('contains').
......5>                  group().
......6>                    by(coalesce(values('marked'), constant('false')))).
......7>              choose(select('true'),
......8>                       coalesce(select('false'), sack()).project('result'),
......9>                       select('false').unfold())).
.....10>     coalesce(select('result').unfold(),
.....11>              sack()).dedup().
.....12>     values('name')
.....13> }
==>groovysh_evaluate$_run_closure1@3f63a513
gremlin> 
gremlin> solution2 = {
......1>   g.V().has('name', 'united states').
......2>     until(select('result').or().not(outE('contains'))).
......3>       repeat(sack(assign).
......4>              map(out('contains').
......5>                  group().
......6>                    by(coalesce(values('marked'), constant('false')))).
......7>              choose(select('true'),
......8>                       union(select('false').unfold(), sack().project('result')),
......9>                       select('false').unfold())).
.....10>     coalesce(select('result').unfold(),
.....11>              sack()).dedup().
.....12>     values('name')
.....13> }
==>groovysh_evaluate$_run_closure1@53f0d09c
gremlin> 
gremlin> solution1()
==>texas
==>los angeles
==>pima
==>maricopa
gremlin> solution2()
==>texas
==>california
==>pima
==>maricopa
gremlin> 
gremlin> g.V().has('name', 'tucson').properties('marked').drop()
gremlin> g.V().has('name', 'pima').property('marked', 'true').iterate()
gremlin> 
gremlin> 
gremlin> solution1()
==>maricopa
==>texas
==>los angeles
gremlin> solution2()
==>arizona
==>texas
==>california
==>maricopa

也许其中一些可以让你走上正确的轨道来解决你的问题,或者答案很简单,Gremlin没有比你目前拥有的更好的解决方案了。

我不确定是否适合您,但我想您可以考虑做一个 subgraph(),然后以这种方式对其进行更改,以便为您提供提示,说明将在树的更下方找到什么,此时您可以更有效地查询子图。我想这样做的成本是否值得付出努力取决于这些树的大小和深度。