1+ // Path Finding - Longest path - Stream - List all dependencies for nodes contributing to longest paths and highlight those paths in the Visualization with GraphViz.
2+
3+ // Gather global statistics about dependency weights and levels for normalization and node details
4+ MATCH (sourceNodeForStatistics )- [ dependencyForStatistics : DEPENDS_ON ] -> (targetNodeForStatistics )
5+ WHERE $dependencies_projection_node IN LABELS (sourceNodeForStatistics )
6+ AND $dependencies_projection_node IN LABELS (targetNodeForStatistics )
7+ WITH min (dependencyForStatistics [$dependencies_projection_weight_property ]) AS minWeight
8+ ,max (dependencyForStatistics [$dependencies_projection_weight_property ]) AS maxWeight
9+ ,max (targetNodeForStatistics .maxDistanceFromSource ) AS maxLevel
10+ WITH * , 1.0 / toFloat (maxWeight - minWeight ) AS weightNormalizationFactor
11+ WITH { minWeight : minWeight , maxLevel : maxLevel , weightNormalizationFactor : weightNormalizationFactor } AS statistics
12+ // -> Main call to execute "longest path" algorithm
13+ CALL gds .dag .longestPath .stream ($dependencies_projection + '-cleaned' )
14+ YIELD index , totalCost , path
15+ WITH *
16+ // Sort longest paths by their length descending and - if equal - by their index ascending
17+ ORDER BY totalCost DESC , index ASC
18+ // Only take the top 50 longest paths as a compromise between performance and visualization content
19+ LIMIT 50
20+ // Collect all results of the longest path search as well as all nodes of the longest paths
21+ WITH statistics
22+ ,collect ({ index : index , distance : toInteger (totalCost ), path : path } ) AS longestPaths
23+ ,collect (nodes (path )) AS allLongestPathNodes
24+ // Flatten and deduplicate the list of all nodes that contribute to at least one longest path
25+ UNWIND allLongestPathNodes AS longestPathNodes
26+ UNWIND longestPathNodes AS longestPathNode
27+ WITH statistics
28+ ,longestPaths
29+ ,collect (DISTINCT longestPathNode ) AS allDistinctLongestPathNodes
30+ // Iterate over all longest paths
31+ UNWIND longestPaths AS longestPath
32+ WITH statistics
33+ ,longestPaths , allDistinctLongestPathNodes
34+ ,[ singleRelationship IN relationships (longestPath .path ) | [startNode (singleRelationship ), endNode (singleRelationship )] ] AS allLongestPathStartAndEndNodeTuples
35+ ,[ singleRelationship IN relationships (longestPaths [0 ].path ) | [startNode (singleRelationship ), endNode (singleRelationship )] ] AS longestPathStartAndEndNodeTuples
36+ ,longestPath .index AS index
37+ ,longestPath .distance AS distance
38+ // -> Main query of all dependencies of nodes contributing to the longest paths
39+ MATCH (source )- [ dependency : DEPENDS_ON ] -> (target )
40+ WHERE $dependencies_projection_node IN labels (source )
41+ AND $dependencies_projection_node IN labels (target )
42+ // Dependent nodes need to be part of at least one longest paths
43+ AND (source IN allDistinctLongestPathNodes AND target IN allDistinctLongestPathNodes )
44+ WITH statistics .maxLevel AS maxLevel
45+ ,statistics .minWeight AS minWeight
46+ ,statistics .weightNormalizationFactor AS weightNormalizationFactor
47+ ,count (index ) AS numberOfLongestPathsPassing
48+ ,max (distance ) AS lengthOfLongestPathPassing
49+ ,dependency
50+ ,source
51+ ,target
52+ // If there is at least one longest path passing through the dependency then "contributesToALongestPath" is true
53+ ,([source , target ] IN allLongestPathStartAndEndNodeTuples ) AS contributesToALongestPath
54+ ,([source , target ] IN longestPathStartAndEndNodeTuples ) AS isPartOfLongestPath
55+ WITH * , dependency [$dependencies_projection_weight_property ] AS weight
56+ WITH * , toFloat (weight - minWeight ) * weightNormalizationFactor AS normalizedWeight
57+ WITH * , round ((normalizedWeight * 5 ) + 1 , 2 ) AS penWidth
58+ WITH * , source .name + "\\ n(level " + source .maxDistanceFromSource + "/" + maxLevel + ")" AS startNodeTitle
59+ WITH * , target .name + "\\ n(level " + target .maxDistanceFromSource + "/" + maxLevel + ")" AS endNodeTitle
60+ // The longest path will be highlighted in red.
61+ WITH * , CASE WHEN isPartOfLongestPath THEN "; color=\" red\" "
62+ // Dependencies contributing to the longest path will be highlighted in dark orange.
63+ WHEN contributesToALongestPath THEN "; color=\" darkorange\" "
64+ ELSE "" END AS edgeColor
65+ // Prepare the GraphViz edge attributes for the visualization
66+ WITH * , "[label=" + weight + "; penwidth=" + penWidth + edgeColor + "; ];" AS graphVizEdgeAttributes
67+ // Assemble the final GraphViz DOT notation line for the edge representing the current dependency
68+ WITH * , "\" " + startNodeTitle + "\" -> \" " + endNodeTitle + "\" " + graphVizEdgeAttributes AS graphVizDotNotationLine
69+ RETURN DISTINCT graphVizDotNotationLine
70+ // Debugging
71+ // ,source.name
72+ // ,target.name
73+ // ,numberOfLongestPathsPassing
74+ // ,lengthOfLongestPathPassing
75+ // ,contributesToALongestPath
76+ // ,isPartOfLongestPath
77+ LIMIT 440
0 commit comments