I found the output of the dependency:packageCycles
constraint shipped with jQAssistant hard to interpret. Specifically I'm keen on finding an example instance of classes that make up the cyclic dependency.
Given I found a cyclce of packages, for each pair of adjunct packages I would need to find two classes that connect them.
This is my first attempt at the Cypher query, but there are still some relevant parts missing:
MATCH nodes = (p1:Package)-[:DEPENDS_ON]->(p2: Package)-[:DEPENDS_ON*]->(p1)
WHERE p1 <> p2
WITH extract(x IN relationships(nodes) |
(:Type)<--(:Package)-[x]->(:Package)-->(:Type)) AS cs
RETURN cs
Specifically, in order to really connect the two packages, the two types should be related to each other with DEPENDS_ON
as shown below:
(:Type)<--(:Package)-[x]->(:Package)-->(:Type)
| ^
| DEPENDS_ON |
+--------------------------------------+
For above pattern I would have to return the two types (and not the packages, for instance). Preferably the output for a single cyclic dependency consists of a list of qualified class names (otherwise multiple one cannot possibly distinguish the class chains of more than one cyclic dependency).
For this specific purpose I find Cypher to be very limited, support for identifying and collecting new graph patterns during path traversal does not seem to be the easiest thing to do. Also the attempt to give names to the (:Type)
nodes resulted in syntax errors.
Also I messed a lot around with UNWIND
, but to no avail. It lets you introduce new MATCH
clauses on per-element basis (say, the elements of relationships(nodes)
), but I do not know of another method to undo the damaging effects of unwind: the surrounding list structure is removed, such that the traces of multiple cyclic dependencies merge into each other. Additionally the results appear permuted to me. That being said below query is conceptually also very close on what I am trying to achieve but does not work:
MATCH nodes = (p1:Package)-[:DEPENDS_ON]->(p2: Package)-[:DEPENDS_ON*]->(p1)
WHERE p1 <> p2
WITH relationships(nodes) as rel
UNWIND rel AS x
MATCH (t0:Type)<-[:CONTAINS]-(:Package)-[x]->(:Package)-[:CONTAINS]->(t1:Type),
(t0)-[:DEPENDS_ON]->(t1)
RETURN t0.fqn, t1.fqn
I do appreciate that there seems to be some scripting support within jQAssistant. However, this would really be my last resort, since it is surely more difficult to maintain than a Cypher query.
To rephrase it: given a path, I'm looking for a method to identify a sub-pattern for each element, project a node out of that match, and to collect the result. Do you have any ideas on how could one accomplish that with Cypher?
Edit #1: Within one package, one has also to consider that the class that is target to the inbound edge of type DEPENDS_ON
may not be the same class that is source to the outgoing edge. In other words, as a result
- two classes of the same package may be part of the trace
- if one wanted to express the cyclic dependency trace as a path, one must take into account detours that navigate to classes in the same package. For instance (edges in bold mark package entry / exit; an edge of type
DEPENDS_ON
is absent between the two types):
-[:DEPENDS_ON]->(:Type)<-[:CONTAINS]-(:Package)-[:CONTAINS]->(:Type)-[DEPENDS_ON]->
Maybe it gets a little clearer using the following picture:
Clearly "a, b, c" is a package cycle and "TestA, TestB1, TestB2, TestC" is a type-level trace for justifying the package-level dependency.