Match 子句是无解的。行为不明确

Match clause is unsolvable. Behavior is not clear

我在运行时以编程方式编译查询,匹配子句是我能想到的执行此任务的更通用的方法。他们中的大多数 运行 都很好,但有些 return "Unsolvable pattern" 例外。

我已经检查过 here 是关于 SO 的唯一类似问题,但它没有回答或解释问题,至少对我来说是这样。

这是我失败的尝试。

g.V().match(
        __.as('loc').has('guid','EGLD').out('instanceOfSupClass').hasLabel('Main_Location'),
        __.as('meter1').out('instanceOfSupClass').hasLabel('Electricity_Meter'),
        __.as('meter2').out('instanceOfSupClass').hasLabel('Electricity_Meter'),
        __.as('class').out('instanceOfSupClass').hasLabel('Cleaned_Electricty_Meter'),
        __.as('meter1').out('hasLocation').as('loc'),
        __.as('meter2').out('isPartOf').as('meter1'),
        __.as('meter2').out('hasTimeSeries').as('class')
    )

与可怕的:

Exception in thread "main" java.lang.IllegalStateException: The provided match pattern is unsolvable: [[MatchStartStep(_m), VertexStep(OUT,[is],vertex), HasStep([~label.eq(A)]), MatchEndStep], [MatchStartStep(_m2), VertexStep(OUT,[bar],edge), StoreStep(edges), EdgeVertexStep(IN), MatchEndStep(_m1)], [MatchStartStep(_m1), VertexStep(OUT,[foo],edge), StoreStep(edges), EdgeVertexStep(IN), MatchEndStep(_l)], [MatchStartStep(c), VertexStep(OUT,[is],vertex), HasStep([~label.eq(D)]), MatchEndStep], [MatchStartStep(_m1), VertexStep(OUT,[is],vertex), HasStep([~label.eq(B)]), MatchEndStep], [MatchStartStep(_l), VertexStep(OUT,[is],vertex), HasStep([~label.eq(A)]), MatchEndStep], [MatchStartStep(_m1), VertexStep(OUT,[beer],edge), StoreStep(edges), EdgeVertexStep(IN), MatchEndStep(c)]]

现在这里有一个类似的可以代替:

g.V().match(
        __.as('loc').has('guid','EGLD').out('instanceOfSupClass').hasLabel('Main_Location'),
        __.as('meter1').out('instanceOfSupClass').hasLabel('Electricity_Meter'),
        __.as('meter2').out('instanceOfSupClass').hasLabel('Electricity_Meter'),
        __.as('class').out('instanceOfSupClass').hasLabel('Cleaned_Electricty_Meter'),
        __.as('meter1').out('hasLocation').as('loc'),
        __.as('meter2').out('isPartOf').as('meter1'),
        __.as('meter1').out('hasTimeSeries').as('class')
    )

我希望他们都return一个结果(尽管不同,因为它们是不同的模式)。

我想了解它失败的原因,并最终了解它是否是错误或我是否遗漏了什么。

编辑: 在玩具图(没有给出错误)和我的真实案例查询(我无法上传我的图示例)之间添加示例和平行关系

beer_graph=TinkerGraph.open()
g = beer_graph.traversal()

A = g.addV('A').next()
B = g.addV('B').next()
C = g.addV('C').next()

LOK = g.addV().next()
MOK1 = g.addV().next()
MOK2 = g.addV().next()
COK = g.addV().next()

g.V(LOK).addE('is').to(A)
g.V(MOK1).addE('is').to(B)
g.V(MOK2).addE('is').to(B)
g.V(COK).addE('is').to(C)

g.V(MOK1).addE('foo').to(LOK)
g.V(MOK2).addE('bar').to(MOK1)
g.V(MOK2).addE('beer').to(COK)

LKO = g.addV().property('guid', 'LKO').next()
MKO1 = g.addV().next()
MKO2 = g.addV().next()
CKO = g.addV().next()

g.V(LKO).addE('is').to(A)
g.V(MKO1).addE('is').to(B)
g.V(MKO2).addE('is').to(B)
g.V(CKO).addE('is').to(C)

g.V(MKO1).addE('foo').to(LKO)
g.V(MKO2).addE('bar').to(MKO1)
g.V(MKO1).addE('beer').to(CKO)


g.V().match(
        __.as('_l').has('guid', 'LKO').outE('is').inV().hasLabel('A'),
        __.as('_m1').outE('is').inV().hasLabel('B'),
        __.as('_m2').outE('is').inV().hasLabel('B'),
        __.as('_c').outE('is').inV().hasLabel('C'),
        __.as('_m1').outE('foo').inV().as('_l'),
        __.as('_m2').outE('bar').inV().as('_m1'),
        __.as('_m1').outE('beer').inV().as('_c')
    )

g.V().match(
        __.as('_l').outE('is').inV().hasLabel('A'),
        __.as('_m1').outE('is').inV().hasLabel('B'),
        __.as('_m2').outE('is').inV().hasLabel('B'),
        __.as('_c').outE('is').inV().hasLabel('C'),
        __.as('_m1').outE('foo').inV().as('_l'),
        __.as('_m2').outE('bar').inV().as('_m1'),
        __.as('_m2').outE('beer').inV().as('_c')
    )   

两者都有效并且 return 正确地得到了结果。现在,远离玩具图,这些是我的项目在运行时构建的遍历:

g.V().match(
        __.as('loc').has('guid','EGLD').out('instanceOfSupClass').hasLabel('Main_Location'),
        __.as('meter1').out('instanceOfSupClass').hasLabel('Electricity_Meter'),
        __.as('meter2').out('instanceOfSupClass').hasLabel('Electricity_Meter'),
        __.as('class').out('instanceOfSupClass').hasLabel('Cleaned_Electricty_Meter'),
        __.as('meter1').out('hasLocation').as('loc'),
        __.as('meter2').out('isPartOf').as('meter1'),
        __.as('meter2').out('hasTimeSeries').as('class')
    )


[
[MatchStartStep(loc), HasStep([guid.eq(EGLD)]), VertexStep(OUT,[instanceOfSupClass],vertex), HasStep([~label.eq(Main_Location)]), MatchEndStep], 
[MatchStartStep(meter1), VertexStep(OUT,[instanceOfSupClass],vertex), HasStep([~label.eq(Electricity_Meter)]), MatchEndStep], 
[MatchStartStep(meter2), VertexStep(OUT,[instanceOfSupClass],vertex), HasStep([~label.eq(Electricity_Meter)]), MatchEndStep], 
[MatchStartStep(class), VertexStep(OUT,[instanceOfSupClass],vertex), HasStep([~label.eq(Cleaned_Electricty_Meter)]), MatchEndStep], 
[MatchStartStep(meter1), VertexStep(OUT,[hasLocation],vertex), MatchEndStep(loc)], 
[MatchStartStep(meter2), VertexStep(OUT,[isPartOf],vertex), MatchEndStep(meter1)], 
[MatchStartStep(meter2), VertexStep(OUT,[hasTimeSeries],vertex), MatchEndStep(class)]
]

与之前编译的相比

g.V().match(
        __.as('_l').has('guid', 'LKO').outE('is').inV().hasLabel('A'),
        __.as('_m1').outE('is').inV().hasLabel('B'),
        __.as('_m2').outE('is').inV().hasLabel('B'),
        __.as('_c').outE('is').inV().hasLabel('C'),
        __.as('_m1').outE('foo').inV().as('_l'),
        __.as('_m2').outE('bar').inV().as('_m1'),
        __.as('_m1').outE('beer').inV().as('_c')
    )

[
[MatchStartStep(_l), HasStep([guid.eq(LKO)]),    VertexStep(OUT,[is],vertex),                   HasStep([~label.eq(A)]), MatchEndStep],
[MatchStartStep(_m1),                VertexStep(OUT,[is],vertex),                   HasStep([~label.eq(B)]), MatchEndStep], 
[MatchStartStep(_m2),                VertexStep(OUT,[is],vertex),                   HasStep([~label.eq(B)]), MatchEndStep], 
[MatchStartStep(_c),                 VertexStep(OUT,[is],vertex),                   HasStep([~label.eq(C)]), MatchEndStep], 
[MatchStartStep(_m1),                VertexStep(OUT,[foo],vertex), MatchEndStep(_l)], 
[MatchStartStep(_m2),                VertexStep(OUT,[bar],vertex), MatchEndStep(_m1)], 
[MatchStartStep(_m1),                VertexStep(OUT,[beer],vertex), MatchEndStep(_c)]
]

为什么 "unsolvable pattern" 异常,为什么更改图表很重要?如果当前图形上的模式是 "unsolvable",我希望结果为空,而不是异常...

编辑 2:发现差异,不理解问题

我设法重建了 "incriminated" 脚本,这个不行...

g.V().match(
        __.as("_loc").has("guid","EGLD").out("instanceOfSupClass").hasLabel("Main_Location"),
        __.as("_meter1").out("instanceOfSupClass").hasLabel("Electricity_Meter"),
        __.as("_meter1").outE("hasLocation").store("edges").inV().as("_loc"),
        __.as("_meter1").outE("hasTimeSeries").store("edges").inV().as("class"),
        __.as("_meter2").out("instanceOfSupClass").hasLabel("Electricity_Meter"),
        __.as("_meter2").outE("isPartOf").store("edges").inV().as("_meter1"),
        __.as("class").out("instanceOfSupClass").hasLabel("Virtual_Anomaly_Class_Time_Series")
    )

这个有效...

g.V().match(
        __.as("_meter2").out("instanceOfSupClass").hasLabel("Electricity_Meter"),
        __.as("_meter2").outE("isPartOf").store("edges").inV().as("_meter1"),
        __.as("_meter1").outE("hasLocation").store("edges").inV().as("_loc"),
        __.as("_meter1").out("instanceOfSupClass").hasLabel("Electricity_Meter"),
        __.as("class").out("instanceOfSupClass").hasLabel("Virtual_Anomaly_Class_Time_Series"),
        __.as("_loc").has("guid","EGLD").out("instanceOfSupClass").hasLabel("Main_Location"),
        __.as("_meter1").outE("hasTimeSeries").store("edges").inV().as("class")
    )

所以顺序很重要

我必须承认,我并不完全理解 MatchStep 代码。这里面有某种排序,但很明显,你不能依靠这种排序把你的模式变成可解的模式。

构建模式时,您应该确保第一个标签与传入元素匹配,并且所有其他 start-labels 都在 start-label 及其连接的 end-labels 之后.这有意义吗?以现代图表为例:

gremlin> g.V().match(
......1>     __.as('a').out('knows').as('b'),
......2>     __.as('b').out('created').as('c'))
==>[a:v[1],b:v[4],c:v[5]]
==>[a:v[1],b:v[4],c:v[3]]
gremlin> g.V().match(
......1>     __.as('b').out('created').as('c'),
......2>     __.as('a').out('knows').as('b'))
The provided match pattern is unsolvable: [[MatchStartStep(a), VertexStep(OUT,[knows],vertex), MatchEndStep(b)], [MatchStartStep(b), VertexStep(OUT,[created],vertex), MatchEndStep(c)]]

第一个模式很容易解决。从 a 可以转到 b,一旦知道 b 是什么,就可以转到 c。现在,第二次遍历只是交换了模式,但很容易看出为什么它无法解决;你从 b 开始,从那里你可以去 c,但是不清楚 a 是从哪里来的。请注意,a.out.b 不能简单地转换为 b.in.a,因为某些图形数据库具有单向边。但是,如果你知道边可以在两个方向上遍历,你手动把它转过来,那么遍历就可以了:

gremlin> g.V().match(
......1>     __.as('b').out('created').as('c'),
......2>     __.as('b').in('knows').as('a'))
==>[a:v[1],b:v[4],c:v[5]]
==>[a:v[1],b:v[4],c:v[3]]

在您的代码示例中,start-label 实际上应该是 _m2 以使所有其他标签都可解析。为什么它有效,即使您没有将其指定为第一个 start-label?我不知道,出于某种原因,内部排序算法给出了正确的模式顺序,但正如我最初提到的,这不应该是预期的。