morpheus icon indicating copy to clipboard operation
morpheus copied to clipboard

Is there any workaround to run cypher "path"

Open ministat opened this issue 1 year ago • 9 comments

Morpheus does not support "path" (https://github.com/opencypher/morpheus/blob/master/documentation/asciidoc/cypher-cypher9-features.adoc), but I encountered a problem which requires "path", I'd like to know is there any workaround.

MATCH path = (start:Application {alias: "r1shsummaryv2"})-[:DependsOn*]->(end:Application {alias: "r1appmetasvccont"})
UNWIND nodes(path) AS node
RETURN node {.alias, .properties} AS valueMap

ministat avatar Sep 16 '23 03:09 ministat

Hello @ministat

It was a long time ago since I (or anyone, really) worked on this library, so I do not recall exactly if we implemented support for variable-length relationship variables. If we did, you could try:

MATCH (start:Application {alias: "r1shsummaryv2"})-[r:DependsOn*]->(end:Application {alias: "r1appmetasvccont"})
UNWIND r AS relationship
WITH startNode(relationship) AS source
RETURN source {.alias, .properties} AS valueMap

Mats-SX avatar Sep 25 '23 13:09 Mats-SX

@Mats-SX Thanks for your reply. Unfortunately, I got exception when I run the cyper you suggested. It looks like the pattern is unsupported.

Exception in thread "main" org.opencypher.okapi.impl.exception.NotImplementedException: Support for pattern conversion of RelationshipChain(NodePattern(Some(Variable(start)),ArrayBuffer(LabelName(Application)),None,None),RelationshipPattern(Some(Variable(r)),List(RelTypeName(DependsOn)),Some(None),None,OUTGOING,false,None),NodePattern(Some(Variable(end)),ArrayBuffer(LabelName(Application)),None,None)) not yet implemented
	at org.opencypher.okapi.ir.impl.PatternConverter$$anonfun$org$opencypher$okapi$ir$impl$PatternConverter$$convertElement$2$$anonfun$apply$3$$anonfun$apply$4$$anonfun$apply$5.apply(PatternConverter.scala:175)
	at org.opencypher.okapi.ir.impl.PatternConverter$$anonfun$org$opencypher$okapi$ir$impl$PatternConverter$$convertElement$2$$anonfun$apply$3$$anonfun$apply$4$$anonfun$apply$5.apply(PatternConverter.scala:127)
	at cats.data.StateFunctions$$anonfun$modify$4.apply(IndexedStateT.scala:299)
	at cats.data.StateFunctions$$anonfun$modify$4.apply(IndexedStateT.scala:299)
	at cats.data.StateFunctions$$anonfun$apply$19.apply(IndexedStateT.scala:289)
	at cats.data.StateFunctions$$anonfun$apply$19.apply(IndexedStateT.scala:289)
	at scala.Function1$$anonfun$andThen$1.apply(Function1.scala:52)
	at cats.data.IndexedStateT$$anonfun$run$1.apply(IndexedStateT.scala:66)
	at cats.data.IndexedStateT$$anonfun$run$1.apply(IndexedStateT.scala:66)
	at cats.Eval$.loop$1(Eval.scala:351)
	at cats.Eval$.cats$Eval$$evaluate(Eval.scala:372)
	at cats.Eval$FlatMap.value(Eval.scala:308)
	at org.opencypher.okapi.ir.impl.PatternConverter.convert(PatternConverter.scala:58)
	at org.opencypher.okapi.ir.impl.IRBuilderContext.convertPattern(IRBuilderContext.scala:64)
	at org.opencypher.okapi.ir.impl.IRBuilder$$anonfun$org$opencypher$okapi$ir$impl$IRBuilder$$convertPattern$1.apply(IRBuilder.scala:546)
	at org.opencypher.okapi.ir.impl.IRBuilder$$anonfun$org$opencypher$okapi$ir$impl$IRBuilder$$convertPattern$1.apply(IRBuilder.scala:544)
	at org.atnos.eff.Continuation.go$1(Continuation.scala:54)
	at org.atnos.eff.Continuation.apply(Continuation.scala:72)
	at org.atnos.eff.Interpret$$anonfun$interpretContinuation$1$1.apply(Interpret.scala:43)
	at org.atnos.eff.Interpret$$anonfun$interpretContinuation$1$1.apply(Interpret.scala:43)
	at org.atnos.eff.Continuation.go$1(Continuation.scala:54)
	at org.atnos.eff.Continuation.apply(Continuation.scala:72)
	at org.atnos.eff.Interpret$$anonfun$interpretContinuation$1$1.apply(Interpret.scala:43)
	at org.atnos.eff.Interpret$$anonfun$interpretContinuation$1$1.apply(Interpret.scala:43)
	at org.atnos.eff.Continuation.go$1(Continuation.scala:54)
	at org.atnos.eff.Continuation.apply(Continuation.scala:72)
	at org.atnos.eff.EffInterpretation$$anonfun$runEval$1$1.apply(Eff.scala:361)
	at org.atnos.eff.EffInterpretation$$anonfun$runEval$1$1.apply(Eff.scala:361)
	at cats.Later.value$lzycompute(Eval.scala:151)
	at cats.Later.value(Eval.scala:150)
	at cats.Eval$.loop$1(Eval.scala:351)
	at cats.Eval$.cats$Eval$$evaluate(Eval.scala:372)
	at cats.Eval$FlatMap.value(Eval.scala:308)
	at org.atnos.eff.EffInterpretation$class.run(Eff.scala:369)
	at org.atnos.eff.Eff$.run(Eff.scala:149)
	at org.atnos.eff.syntax.EffNoEffectOps$.run$extension(eff.scala:59)
	at org.opencypher.okapi.ir.impl.package$RichIRBuilderStack.run(package.scala:52)
	at org.opencypher.okapi.ir.impl.IRBuilder$.process(IRBuilder.scala:56)
	at org.opencypher.okapi.relational.api.graph.RelationalCypherSession$$anonfun$6.apply(RelationalCypherSession.scala:169)
	at org.opencypher.okapi.relational.api.graph.RelationalCypherSession$$anonfun$6.apply(RelationalCypherSession.scala:169)
	at org.opencypher.okapi.relational.api.graph.RelationalCypherSession.time(RelationalCypherSession.scala:119)
	at org.opencypher.okapi.relational.api.graph.RelationalCypherSession.cypherOnGraph(RelationalCypherSession.scala:169)
	at org.opencypher.okapi.relational.api.graph.RelationalCypherGraph$class.cypher(RelationalCypherGraph.scala:106)
	at org.opencypher.okapi.relational.impl.graph.ScanGraph.cypher(ScanGraph.scala:43)
	at org.opencypher.okapi.relational.impl.graph.ScanGraph.cypher(ScanGraph.scala:43)

ministat avatar Sep 25 '23 14:09 ministat

As a last attempt, try adding an upper bound to the variable-length pattern. I noted that we declared unbounded var-length patterns as not implemented here.

MATCH (start:Application {alias: "r1shsummaryv2"})-[r:DependsOn*..100]->(end:Application {alias: "r1appmetasvccont"})
UNWIND r AS relationship
WITH startNode(relationship) AS source
RETURN source {.alias, .properties} AS valueMap

Mats-SX avatar Sep 26 '23 07:09 Mats-SX

The exception changed, but unfortunately, it still failed for something not implemented.

23/09/27 10:25:12 WARN SparkSession$Builder: Using an existing SparkSession; some configuration may not take effect.
Exception in thread "main" org.opencypher.okapi.impl.exception.NotImplementedException: The expression DesugaredMapProjection(Variable(source),List(LiteralEntry(PropertyKeyName(alias),Property(Variable(source),PropertyKeyName(alias))), LiteralEntry(PropertyKeyName(properties),Property(Variable(source),PropertyKeyName(properties)))),false) [line 4, column 8 (offset: 189)] is not supported by the system
	at org.opencypher.okapi.ir.impl.IRBuilderContext.infer(IRBuilderContext.scala:84)
	at org.opencypher.okapi.ir.impl.IRBuilderContext.convertExpression(IRBuilderContext.scala:68)
	at org.opencypher.okapi.ir.impl.IRBuilder$$anonfun$org$opencypher$okapi$ir$impl$IRBuilder$$convertExpr$2.apply(IRBuilder.scala:567)
	at org.opencypher.okapi.ir.impl.IRBuilder$$anonfun$org$opencypher$okapi$ir$impl$IRBuilder$$convertExpr$2.apply(IRBuilder.scala:566)
	at org.atnos.eff.Continuation$$anonfun$map$1.apply(Continuation.scala:39)
	at org.atnos.eff.Continuation$$anonfun$map$1.apply(Continuation.scala:39)
	at org.atnos.eff.Continuation.go$1(Continuation.scala:54)
	at org.atnos.eff.Continuation.apply(Continuation.scala:72)
	at org.atnos.eff.Interpret$$anonfun$interpretContinuation$1$1.apply(Interpret.scala:43)
	at org.atnos.eff.Interpret$$anonfun$interpretContinuation$1$1.apply(Interpret.scala:43)
	at org.atnos.eff.Continuation.go$1(Continuation.scala:54)
	at org.atnos.eff.Continuation.apply(Continuation.scala:72)
	at org.atnos.eff.Interpret$$anonfun$interpretContinuation$1$1.apply(Interpret.scala:43)
	at org.atnos.eff.Interpret$$anonfun$interpretContinuation$1$1.apply(Interpret.scala:43)
	at org.atnos.eff.Continuation.go$1(Continuation.scala:54)
	at org.atnos.eff.Continuation.apply(Continuation.scala:72)
	at org.atnos.eff.EffInterpretation$$anonfun$runEval$1$1.apply(Eff.scala:361)
	at org.atnos.eff.EffInterpretation$$anonfun$runEval$1$1.apply(Eff.scala:361)
	at cats.Later.value$lzycompute(Eval.scala:151)
	at cats.Later.value(Eval.scala:150)
	at cats.Eval$.loop$1(Eval.scala:351)
	at cats.Eval$.cats$Eval$$evaluate(Eval.scala:372)
	at cats.Eval$FlatMap.value(Eval.scala:308)
	at org.atnos.eff.EffInterpretation$class.run(Eff.scala:369)
	at org.atnos.eff.Eff$.run(Eff.scala:149)
	at org.atnos.eff.syntax.EffNoEffectOps$.run$extension(eff.scala:59)
	at org.opencypher.okapi.ir.impl.package$RichIRBuilderStack.run(package.scala:52)
	at org.opencypher.okapi.ir.impl.IRBuilder$.process(IRBuilder.scala:56)
	at org.opencypher.okapi.relational.api.graph.RelationalCypherSession$$anonfun$6.apply(RelationalCypherSession.scala:169)
	at org.opencypher.okapi.relational.api.graph.RelationalCypherSession$$anonfun$6.apply(RelationalCypherSession.scala:169)
	at org.opencypher.okapi.relational.api.graph.RelationalCypherSession.time(RelationalCypherSession.scala:119)
	at org.opencypher.okapi.relational.api.graph.RelationalCypherSession.cypherOnGraph(RelationalCypherSession.scala:169)
	at org.opencypher.okapi.relational.api.graph.RelationalCypherGraph$class.cypher(RelationalCypherGraph.scala:106)
	at org.opencypher.okapi.relational.impl.graph.ScanGraph.cypher(ScanGraph.scala:43)
	at org.opencypher.okapi.relational.impl.graph.ScanGraph.cypher(ScanGraph.scala:43)
	at com.ebay.nugraph.gremlintomorpheus.Sherlock$.executeCypher(Sherlock.scala:260)
	at com.ebay.nugraph.gremlintomorpheus.MainEntry$.main(MainEntry.scala:16)
	at com.ebay.nugraph.gremlintomorpheus.MainEntry.main(MainEntry.scala)

ministat avatar Sep 27 '23 02:09 ministat

@Mats-SX Do you have any document introducing the design and implementation? I'd like to evaluate the effort to support this feature. That will be great if you or your colleagues can provide some help and guide.

ministat avatar Sep 27 '23 02:09 ministat

I think you are quite close, this should work:

MATCH (start:Application {alias: "r1shsummaryv2"})-[r:DependsOn*..100]->(end:Application {alias: "r1appmetasvccont"})
UNWIND r AS relationship
WITH startNode(relationship) AS source
RETURN source.alias, source.properties

There is one caveat though. IIRC the Variable Path Expand will always try to do as many expands as you specify, because we basically unroll this into optional joins in spark. So you might want to play around with that upper bound to get faster query results

DarthMax avatar Sep 28 '23 07:09 DarthMax

Yes. It looks quite close to be working. However, there is an exception. Can I ignore this?

Exception in thread "main" org.opencypher.okapi.impl.exception.IllegalArgumentException: 

Expected:
	Header does not contain a column for r :: LIST(RELATIONSHIP(:DependsOn) @ session.tmp1).
	[`end._label_`, `end.alias`, `end.label`, `end.lcmstate`, `end.name`, `end.type`, `end:Application`, `end`, `explode(r)`, `r(1).edgelabel`, `r(1):DependsOn`, `r(1)`, `r(2).edgelabel`, `r(2):DependsOn`, `r(2)`, `relationship`, `source(r(1))`, `source(r(2))`, `start._label_`, `start.alias`, `start.label`, `start.lcmstate`, `start.name`, `start.type`, `start:Application`, `start`, `target(r(1))`, `target(r(2))`]
Found:
	none
	at org.opencypher.okapi.relational.impl.table.RecordHeader$$anonfun$column$1.apply(RecordHeader.scala:88)
	at org.opencypher.okapi.relational.impl.table.RecordHeader$$anonfun$column$1.apply(RecordHeader.scala:88)
	at scala.collection.MapLike$class.getOrElse(MapLike.scala:128)
	at scala.collection.AbstractMap.getOrElse(Map.scala:59)
	at org.opencypher.okapi.relational.impl.table.RecordHeader.column(RecordHeader.scala:88)
	at org.opencypher.spark.impl.SparkSQLExprMapper$RichExpression.asSparkSQLExpr(SparkSQLExprMapper.scala:133)
	at org.opencypher.spark.impl.SparkSQLExprMapper$RichExpression.asSparkSQLExpr(SparkSQLExprMapper.scala:371)
	at org.opencypher.spark.impl.SparkSQLExprMapper$RichExpression.asSparkSQLExpr(SparkSQLExprMapper.scala:141)
	at org.opencypher.spark.impl.table.SparkTable$DataFrameTable$$anonfun$4.apply(SparkTable.scala:85)
	at org.opencypher.spark.impl.table.SparkTable$DataFrameTable$$anonfun$4.apply(SparkTable.scala:84)
	at scala.collection.LinearSeqOptimized$class.foldLeft(LinearSeqOptimized.scala:124)
	at scala.collection.immutable.List.foldLeft(List.scala:84)
	at org.opencypher.spark.impl.table.SparkTable$DataFrameTable.withColumns(SparkTable.scala:84)
	at org.opencypher.spark.impl.table.SparkTable$DataFrameTable.withColumns(SparkTable.scala:53)
	at org.opencypher.okapi.relational.impl.operators.Add._table$lzycompute(RelationalOperator.scala:246)
	at org.opencypher.okapi.relational.impl.operators.Add._table(RelationalOperator.scala:240)
	at org.opencypher.okapi.relational.impl.operators.RelationalOperator.table(RelationalOperator.scala:77)
	at org.opencypher.okapi.relational.impl.operators.Add._table$lzycompute(RelationalOperator.scala:246)
	at org.opencypher.okapi.relational.impl.operators.Add._table(RelationalOperator.scala:240)
	at org.opencypher.okapi.relational.impl.operators.RelationalOperator.table(RelationalOperator.scala:77)
	at org.opencypher.okapi.relational.impl.operators.Add._table$lzycompute(RelationalOperator.scala:246)
	at org.opencypher.okapi.relational.impl.operators.Add._table(RelationalOperator.scala:240)
	at org.opencypher.okapi.relational.impl.operators.RelationalOperator.table(RelationalOperator.scala:77)
	at org.opencypher.okapi.relational.impl.operators.Add._table$lzycompute(RelationalOperator.scala:246)
	at org.opencypher.okapi.relational.impl.operators.Add._table(RelationalOperator.scala:240)
	at org.opencypher.okapi.relational.impl.operators.RelationalOperator.table(RelationalOperator.scala:77)
	at org.opencypher.okapi.relational.impl.operators.Select._table$lzycompute(RelationalOperator.scala:330)
	at org.opencypher.okapi.relational.impl.operators.Select._table(RelationalOperator.scala:328)
	at org.opencypher.okapi.relational.impl.operators.RelationalOperator.table(RelationalOperator.scala:77)
	at org.opencypher.okapi.relational.impl.operators.AlignColumnsWithReturnItems._table$lzycompute(RelationalOperator.scala:355)
	at org.opencypher.okapi.relational.impl.operators.AlignColumnsWithReturnItems._table(RelationalOperator.scala:353)
	at org.opencypher.okapi.relational.impl.operators.RelationalOperator.table(RelationalOperator.scala:77)
	at org.opencypher.okapi.relational.api.planning.RelationalCypherResult$$anonfun$getRecords$1.apply(RelationalCypherResult.scala:70)
	at org.opencypher.okapi.relational.api.planning.RelationalCypherResult$$anonfun$getRecords$1.apply(RelationalCypherResult.scala:64)
	at scala.Option.flatMap(Option.scala:171)
	at org.opencypher.okapi.relational.api.planning.RelationalCypherResult.getRecords(RelationalCypherResult.scala:64)
	at org.opencypher.okapi.api.graph.CypherResult$class.records(CypherResult.scala:71)
	at org.opencypher.okapi.relational.api.planning.RelationalCypherResult.records(RelationalCypherResult.scala:38)
	at com.ebay.nugraph.gremlintomorpheus.Sherlock$.executeCypher(Sherlock.scala:270)
	at com.ebay.nugraph.gremlintomorpheus.MainEntry$.main(MainEntry.scala:16)
	at com.ebay.nugraph.gremlintomorpheus.MainEntry.main(MainEntry.scala)

Source code in SparkSQLExprMapper.scala:

        case _: Var | _: Param | _: HasLabel | _: HasType | _: StartNode | _: EndNode =>
          verify

          val colName = header.column(expr) // <- throw exception
          if (df.columns.contains(colName)) {
            df.col(colName)
          } else {
            NULL_LIT
          }

ministat avatar Sep 28 '23 08:09 ministat

I really don't recall how this works. It looks like we're expanding the relationship variable r into multiple variables r(x) where x denotes the distance from the source node. So with an upper bound of 10 you would get 10 such r variables. But we're not renaming things well enough, because explode(r) references just r not r(x) which cannot be found.

I couldn't say what is wrong -- my best advice is to try some more alternatives, maybe this one:

MATCH (start:Application {alias: "r1shsummaryv2"})-[r:DependsOn*..2]->(end:Application {alias: "r1appmetasvccont"})
RETURN r

But it could also be the case that what you're trying to do is not properly implemented. In case you want to implement more support feel free to go ahead. At Neo4j we are not pursuing anything with this project currently. We do have another project https://github.com/neo4j-contrib/neo4j-spark-connector which is actively maintained. I've contacted the people who work on that to see if it can help your use case.

Mats-SX avatar Oct 03 '23 14:10 Mats-SX

Thanks for your patient response. The following cypher does not throw exception nor give the expected result.

MATCH (start:Application {alias: "r1shsummaryv2"})-[r:DependsOn*..2]->(end:Application {alias: "r1appmetasvccont"})
RETURN r
╔══════╤══════╤══════════════════════════════════════════════════════════════════════════════════════════╗
║ r(1) │ r(2) │ r                                                                                        ║
╠══════╪══════╪══════════════════════════════════════════════════════════════════════════════════════════╣
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}]]]                                            ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
║ null │ null │ [[[:`DependsOn` {`edgelabel`: 'DependsOn'}], [:`DependsOn` {`edgelabel`: 'DependsOn'}]]] ║
╚══════╧══════╧══════════════════════════════════════════════════════════════════════════════════════════╝

ministat avatar Oct 04 '23 05:10 ministat