From 586c5e34390c4cad6b1ba2efe7d8a97d6c19e746 Mon Sep 17 00:00:00 2001 From: JohT <7671054+JohT@users.noreply.github.com> Date: Sun, 26 Oct 2025 13:09:15 +0100 Subject: [PATCH] Fix missing Graph visualizations with optional links and parametrized weight property --- .../graphs/TopAuthority.cypher | 20 +++--- .../graphs/TopBottleneck.cypher | 26 ++++---- .../anomaly-detection/graphs/TopBridge.cypher | 20 +++--- .../anomaly-detection/graphs/TopHub.cypher | 61 ++++++++++++++----- .../graphs/TopOutlier.cypher | 18 +++--- .../anomalyDetectionGraphVisualization.sh | 23 ++++--- 6 files changed, 103 insertions(+), 65 deletions(-) diff --git a/domains/anomaly-detection/graphs/TopAuthority.cypher b/domains/anomaly-detection/graphs/TopAuthority.cypher index 8582287d9..b45e29dc5 100644 --- a/domains/anomaly-detection/graphs/TopAuthority.cypher +++ b/domains/anomaly-detection/graphs/TopAuthority.cypher @@ -4,7 +4,7 @@ MATCH (sourceForStatistics)-[dependencyForStatistics:DEPENDS_ON]->(targetForStatistics) WHERE $projection_node_label IN labels(sourceForStatistics) AND $projection_node_label IN labels(targetForStatistics) - WITH max(coalesce(dependencyForStatistics.weight25PercentInterfaces, dependencyForStatistics.weight)) AS maxWeight + WITH max(coalesce(dependencyForStatistics[$projection_weight_property])) AS maxWeight ,percentileDisc(sourceForStatistics.centralityPageRankToArticleRankDifference, 0.80) AS pageToArticleRankThreshold ,percentileDisc(targetForStatistics.centralityPageRankNormalized, 0.80) AS pageRankThreshold // Step 2: Query selected central node @@ -26,12 +26,12 @@ WITH *, "🏛️ authority #" + central.anomalyAuthorityRank + "\\n" + central.name AS centralNodeLabel WITH *, graphVizOutput + ["central [label=\"" + centralNodeLabel + "\"];"] AS graphVizOutput // Step 3: Query direct incoming dependencies to the central node - MATCH (source)-[dependency:DEPENDS_ON]->(central) +OPTIONAL MATCH (source)-[dependency:DEPENDS_ON]->(central) WHERE $projection_node_label IN labels(source) AND source.outgoingDependencies > 0 - ORDER BY dependency.weight DESC, source.name ASC + ORDER BY dependency[$projection_weight_property] DESC, source.name ASC LIMIT 40 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes WITH *, CASE WHEN source.centralityPageRankToArticleRankDifference >= pageToArticleRankThreshold @@ -56,12 +56,12 @@ ,collect(directInNode + "\"" + sourceId + "\" -> central [" + edgeAttributes + "];") AS directInEdges WITH *, graphVizOutput + directInEdges AS graphVizOutput // Step 4: Query direct outgoing dependencies from the central node - MATCH (source)<-[dependency:DEPENDS_ON]-(central) +OPTIONAL MATCH (source)<-[dependency:DEPENDS_ON]-(central) WHERE $projection_node_label IN labels(source) AND source.incomingDependencies > 0 - ORDER BY dependency.weight DESC, source.name ASC + ORDER BY dependency[$projection_weight_property] DESC, source.name ASC LIMIT 40 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes // Use a lighter color for the target nodes of outgoing dependencies from the central node and their edges @@ -90,16 +90,16 @@ WITH *, incomingDependencyNodes + outgoingDependencyNodes AS directDependentNodes // Step 5: Query dependencies between direct dependencies outside the central node UNWIND directDependentNodes AS directDependentNode - MATCH (directDependentNode)-[dependency:DEPENDS_ON]->(anotherDirectDependentNode) +OPTIONAL MATCH (directDependentNode)-[dependency:DEPENDS_ON]->(anotherDirectDependentNode) WHERE anotherDirectDependentNode IN directDependentNodes AND anotherDirectDependentNode <> directDependentNode - ORDER BY dependency.weight DESC, directDependentNode.name ASC + ORDER BY dependency[$projection_weight_property] DESC, directDependentNode.name ASC WITH graphVizOutput ,directDependentNode ,dependency ,collect(anotherDirectDependentNode)[0] AS firstLinkedDependentNode LIMIT 140 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight // Use a fixed small pen width for secondary dependencies for better visibility of the more important direct dependency WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=0.3" AS edgeAttributes // Use an even lighter color for secondary dependency edges diff --git a/domains/anomaly-detection/graphs/TopBottleneck.cypher b/domains/anomaly-detection/graphs/TopBottleneck.cypher index ff9e08e5b..2746ecff3 100644 --- a/domains/anomaly-detection/graphs/TopBottleneck.cypher +++ b/domains/anomaly-detection/graphs/TopBottleneck.cypher @@ -4,8 +4,8 @@ MATCH (sourceForStatistics)-[dependencyForStatistics:DEPENDS_ON]->(targetForStatistics) WHERE $projection_node_label IN labels(sourceForStatistics) AND $projection_node_label IN labels(targetForStatistics) - WITH max(coalesce(dependencyForStatistics.weight25PercentInterfaces, dependencyForStatistics.weight)) AS maxWeight - ,percentileDisc(sourceForStatistics.centralityBetweenness, 0.90) AS betweennessThreshold + WITH max(coalesce(dependencyForStatistics[$projection_weight_property])) AS maxWeight + ,percentileDisc(sourceForStatistics.centralityBetweenness, 0.90) AS betweennessThreshold // Step 2: Query selected central node MATCH (central) WHERE $projection_node_label IN labels(central) @@ -23,12 +23,12 @@ WITH *, "🔒 bottleneck #" + central.anomalyBottleneckRank + "\\n" + central.name AS centralNodeLabel WITH *, graphVizOutput + ["central [label=\"" + centralNodeLabel + "\"];"] AS graphVizOutput // Step 3: Query direct incoming dependencies to the central node - MATCH (source)-[dependency:DEPENDS_ON]->(central) +OPTIONAL MATCH (source)-[dependency:DEPENDS_ON]->(central) WHERE $projection_node_label IN labels(source) AND source.outgoingDependencies > 0 - ORDER BY dependency.weight DESC, source.name ASC + ORDER BY dependency[$projection_weight_property] DESC, source.name ASC LIMIT 40 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes WITH *, CASE WHEN source.centralityBetweenness >= betweennessThreshold @@ -50,12 +50,12 @@ ,collect(directInNode + "\"" + sourceId + "\" -> central [" + edgeAttributes + "];") AS directInEdges WITH *, graphVizOutput + directInEdges AS graphVizOutput // Step 4: Query direct outgoing dependencies from the central node - MATCH (source)<-[dependency:DEPENDS_ON]-(central) +OPTIONAL MATCH (source)<-[dependency:DEPENDS_ON]-(central) WHERE $projection_node_label IN labels(source) AND source.incomingDependencies > 0 - ORDER BY dependency.weight DESC, source.name ASC + ORDER BY dependency[$projection_weight_property] DESC, source.name ASC LIMIT 40 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes // Use a lighter color for the target nodes of outgoing dependencies from the central node and their edges @@ -72,9 +72,7 @@ WITH *, "label = \"" + sourceNameSplit + "\\n(" + labelValue + ")\"; " AS directOutLabel WITH *, " [" + directOutLabel + directOutBorder + directOutColor + "]; " AS directOutNodeProperties WITH *, "\"" + sourceId + "\" " + directOutNodeProperties AS directOutNode - WITH maxWeight - ,betweennessThreshold - ,central + WITH central ,graphVizOutput ,incomingDependencyNodes ,collect(source) AS outgoingDependencyNodes @@ -83,16 +81,16 @@ WITH *, incomingDependencyNodes + outgoingDependencyNodes AS directDependentNodes // Step 5: Query dependencies between direct dependencies outside the central node UNWIND directDependentNodes AS directDependentNode - MATCH (directDependentNode)-[dependency:DEPENDS_ON]->(anotherDirectDependentNode) +OPTIONAL MATCH (directDependentNode)-[dependency:DEPENDS_ON]->(anotherDirectDependentNode) WHERE anotherDirectDependentNode IN directDependentNodes AND anotherDirectDependentNode <> directDependentNode - ORDER BY dependency.weight DESC, directDependentNode.name ASC + ORDER BY dependency[$projection_weight_property] DESC, directDependentNode.name ASC WITH graphVizOutput ,directDependentNode ,dependency ,collect(anotherDirectDependentNode)[0] AS firstLinkedDependentNode LIMIT 140 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight // Use a fixed small pen width for secondary dependencies for better visibility of the more important direct dependency WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=0.3" AS edgeAttributes // Use an even lighter color for secondary dependency edges diff --git a/domains/anomaly-detection/graphs/TopBridge.cypher b/domains/anomaly-detection/graphs/TopBridge.cypher index 4f2f8ea0e..818221c96 100644 --- a/domains/anomaly-detection/graphs/TopBridge.cypher +++ b/domains/anomaly-detection/graphs/TopBridge.cypher @@ -4,7 +4,7 @@ MATCH (sourceForStatistics)-[dependencyForStatistics:DEPENDS_ON]->(targetForStatistics) WHERE $projection_node_label IN labels(sourceForStatistics) AND $projection_node_label IN labels(targetForStatistics) - WITH max(coalesce(dependencyForStatistics.weight25PercentInterfaces, dependencyForStatistics.weight)) AS maxWeight + WITH max(coalesce(dependencyForStatistics[$projection_weight_property])) AS maxWeight // Step 2: Query selected central node MATCH (central) WHERE $projection_node_label IN labels(central) @@ -19,12 +19,12 @@ WITH *, "🌉 bridge #" + central.anomalyBridgeRank + "\\n" + central.name AS centralNodeLabel WITH *, graphVizOutput + ["central [label=\"" + centralNodeLabel + "\"];"] AS graphVizOutput // Step 3: Query direct incoming dependencies to the central node - MATCH (source)-[dependency:DEPENDS_ON]->(central) +OPTIONAL MATCH (source)-[dependency:DEPENDS_ON]->(central) WHERE $projection_node_label IN labels(source) AND source.outgoingDependencies > 0 - ORDER BY dependency.weight DESC, source.name ASC + ORDER BY dependency[$projection_weight_property] DESC, source.name ASC LIMIT 40 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes // Add the last part of the element id to the node name to make it unique. @@ -41,12 +41,12 @@ ,collect(directInNode + "\"" + sourceId + "\" -> central [" + edgeAttributes + "];") AS directInEdges WITH *, graphVizOutput + directInEdges AS graphVizOutput // Step 4: Query direct outgoing dependencies from the central node - MATCH (source)<-[dependency:DEPENDS_ON]-(central) +OPTIONAL MATCH (source)<-[dependency:DEPENDS_ON]-(central) WHERE $projection_node_label IN labels(source) AND source.incomingDependencies > 0 - ORDER BY dependency.weight DESC, source.name ASC + ORDER BY dependency[$projection_weight_property] DESC, source.name ASC LIMIT 40 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes // Use a lighter color for the target nodes of outgoing dependencies from the central node and their edges @@ -70,16 +70,16 @@ WITH *, incomingDependencyNodes + outgoingDependencyNodes AS directDependentNodes // Step 5: Query dependencies between direct dependencies outside the central node UNWIND directDependentNodes AS directDependentNode - MATCH (directDependentNode)-[dependency:DEPENDS_ON]->(anotherDirectDependentNode) +OPTIONAL MATCH (directDependentNode)-[dependency:DEPENDS_ON]->(anotherDirectDependentNode) WHERE anotherDirectDependentNode IN directDependentNodes AND anotherDirectDependentNode <> directDependentNode - ORDER BY dependency.weight DESC, directDependentNode.name ASC + ORDER BY dependency[$projection_weight_property] DESC, directDependentNode.name ASC WITH graphVizOutput ,directDependentNode ,dependency ,collect(anotherDirectDependentNode)[0] AS firstLinkedDependentNode LIMIT 140 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight // Use a fixed small pen width for secondary dependencies for better visibility of the more important direct dependency WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=0.3" AS edgeAttributes // Use an even lighter color for secondary dependency edges diff --git a/domains/anomaly-detection/graphs/TopHub.cypher b/domains/anomaly-detection/graphs/TopHub.cypher index aa9f971eb..86dc94728 100644 --- a/domains/anomaly-detection/graphs/TopHub.cypher +++ b/domains/anomaly-detection/graphs/TopHub.cypher @@ -1,10 +1,10 @@ -// Anomaly Detection Graphs: Find top nodes marked as "central" including their incoming dependencies and output them in Graphviz format. +// Anomaly Detection Graphs: Find top nodes marked as "Hub" including their incoming and outgoing dependencies and output them in Graphviz format. // Step 1: Query overall statistics, e.g. min/max weight for later normalization MATCH (sourceForStatistics)-[dependencyForStatistics:DEPENDS_ON]->(targetForStatistics) WHERE $projection_node_label IN labels(sourceForStatistics) AND $projection_node_label IN labels(targetForStatistics) - WITH max(coalesce(dependencyForStatistics.weight25PercentInterfaces, dependencyForStatistics.weight)) AS maxWeight + WITH max(coalesce(dependencyForStatistics[$projection_weight_property])) AS maxWeight ,percentileDisc(sourceForStatistics.communityLocalClusteringCoefficient, 0.10) AS localClusteringCoefficientLowThreshold // Step 2: Query selected central node MATCH (central) @@ -23,12 +23,12 @@ WITH *, centralNodeLabel + "\\n(in-degree=" + central.incomingDependencies + ")" AS centralNodeLabel WITH *, graphVizOutput + ["central [label=\"" + centralNodeLabel + "\"];"] AS graphVizOutput // Step 3: Query direct incoming dependencies to the central node - MATCH (source)-[dependency:DEPENDS_ON]->(central) +OPTIONAL MATCH (source)-[dependency:DEPENDS_ON]->(central) WHERE $projection_node_label IN labels(source) AND source.outgoingDependencies > 0 - ORDER BY dependency.weight DESC, source.name ASC - LIMIT 50 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + ORDER BY dependency[$projection_weight_property] DESC, source.name ASC + LIMIT 40 + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes WITH *, CASE WHEN source.communityLocalClusteringCoefficient <= localClusteringCoefficientLowThreshold @@ -36,32 +36,65 @@ WITH *, round(source.communityLocalClusteringCoefficient, 2) AS labelValue // Add the last part of the element id to make the node name unique, even if the name itself isn't. WITH *, source.name + "_" + split(elementId(source), ':')[-1] AS sourceId - // Split long names like inner classes identified by a dollar sign ($) WITH *, "penwidth = " + scaledNodeBorder + "; " AS directInBorder + // Split long names like inner classes identified by a dollar sign ($) WITH *, replace(source.name, '$', '$\\n') AS sourceNameSplit WITH *, "label = \"" + sourceNameSplit + "\\n(" + labelValue + ")\"; " AS directInLabel WITH *, " [" + directInLabel + directInBorder + "]; " AS directInNodeProperties WITH *, "\"" + sourceId + "\" " + directInNodeProperties AS directInNode WITH maxWeight + ,localClusteringCoefficientLowThreshold ,central ,graphVizOutput - ,collect(source) AS directDependentNodes + ,collect(source) AS incomingDependencyNodes ,collect(directInNode + "\"" + sourceId + "\" -> central [" + edgeAttributes + "];") AS directInEdges WITH *, graphVizOutput + directInEdges AS graphVizOutput -// Step 4: Query dependencies between direct dependencies outside the central node +// Step 4: Query direct outgoing dependencies from the central node +OPTIONAL MATCH (source)<-[dependency:DEPENDS_ON]-(central) + WHERE $projection_node_label IN labels(source) + AND source.incomingDependencies > 0 + ORDER BY dependency[$projection_weight_property] DESC, source.name ASC + LIMIT 40 + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight + WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth + WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes + // Use a lighter color for the target nodes of outgoing dependencies from the central node and their edges + WITH *, edgeAttributes + "; color = 5" AS edgeAttributes + WITH *, "color = 5; fillcolor = 1; " AS directOutColor + WITH *, CASE WHEN source.communityLocalClusteringCoefficient <= localClusteringCoefficientLowThreshold + THEN 5 ELSE 2 END AS scaledNodeBorder + WITH *, round(source.communityLocalClusteringCoefficient, 2) AS labelValue + // Add the last part of the element id to the node name to make it unique. + WITH *, source.name + "_" + split(elementId(source), ':')[-1] AS sourceId + WITH *, "penwidth = " + scaledNodeBorder + "; " AS directOutBorder + // Split long names like inner classes identified by a dollar sign ($) + WITH *, replace(source.name, '$', '$\\n') AS sourceNameSplit + WITH *, "label = \"" + sourceNameSplit + "\\n(" + labelValue + ")\"; " AS directOutLabel + WITH *, " [" + directOutLabel + directOutBorder + directOutColor + "]; " AS directOutNodeProperties + WITH *, "\"" + sourceId + "\" " + directOutNodeProperties AS directOutNode + WITH central + ,graphVizOutput + ,incomingDependencyNodes + ,collect(source) AS outgoingDependencyNodes + ,collect(directOutNode + "central -> \"" + sourceId + "\" [" + edgeAttributes + "];") AS directOutEdges + WITH *, graphVizOutput + directOutEdges AS graphVizOutput + WITH *, incomingDependencyNodes + outgoingDependencyNodes AS directDependentNodes +// Step 5: Query dependencies between direct dependencies outside the central node UNWIND directDependentNodes AS directDependentNode - MATCH (directDependentNode)-[dependency:DEPENDS_ON]->(anotherDirectDependentNode) +OPTIONAL MATCH (directDependentNode)-[dependency:DEPENDS_ON]->(anotherDirectDependentNode) WHERE anotherDirectDependentNode IN directDependentNodes AND anotherDirectDependentNode <> directDependentNode - ORDER BY dependency.weight DESC, directDependentNode.name ASC + ORDER BY dependency[$projection_weight_property] DESC, directDependentNode.name ASC WITH graphVizOutput ,directDependentNode ,dependency ,collect(anotherDirectDependentNode)[0] AS firstLinkedDependentNode LIMIT 140 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight -// Use a fixed small pen width for secondary dependencies for better visibility of the more important direct dependency - WITH *, "weight=" + weight + "; penwidth=0.2" AS edgeAttributes + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight + // Use a fixed small pen width for secondary dependencies for better visibility of the more important direct dependency + WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=0.3" AS edgeAttributes + // Use an even lighter color for secondary dependency edges + WITH *, edgeAttributes + "; color = 3" AS edgeAttributes // Add the last part of the element id to the node name to make it unique. WITH *, directDependentNode.name + "_" + split(elementId(directDependentNode), ':')[-1] AS directDependentNodeId WITH *, firstLinkedDependentNode.name + "_" + split(elementId(firstLinkedDependentNode), ':')[-1] AS firstLinkedDependentNodeId diff --git a/domains/anomaly-detection/graphs/TopOutlier.cypher b/domains/anomaly-detection/graphs/TopOutlier.cypher index 9b72085d3..e485a9879 100644 --- a/domains/anomaly-detection/graphs/TopOutlier.cypher +++ b/domains/anomaly-detection/graphs/TopOutlier.cypher @@ -4,7 +4,7 @@ MATCH (sourceForStatistics)-[dependencyForStatistics:DEPENDS_ON]->(targetForStatistics) WHERE $projection_node_label IN labels(sourceForStatistics) AND $projection_node_label IN labels(targetForStatistics) - WITH max(coalesce(dependencyForStatistics.weight25PercentInterfaces, dependencyForStatistics.weight)) AS maxWeight + WITH max(coalesce(dependencyForStatistics[$projection_weight_property])) AS maxWeight ,percentileDisc(sourceForStatistics.clusteringHDBSCANNormalizedDistanceToMedoid, 0.80) AS clusterMedoidDistanceThreshold ,percentileDisc(sourceForStatistics.clusteringHDBSCANProbability, 0.25) AS clusterProbabilityLowThreshold // Step 2: Query selected central node @@ -26,12 +26,12 @@ WITH *, "🧩 outlier #" + central.anomalyOutlierRank + "\\n" + central.name AS centralNodeLabel WITH *, graphVizOutput + ["central [label=\"" + centralNodeLabel + "\"];"] AS graphVizOutput // Step 3: Query direct incoming dependencies to the central node - MATCH (source)-[dependency:DEPENDS_ON]->(central) +OPTIONAL MATCH (source)-[dependency:DEPENDS_ON]->(central) WHERE $projection_node_label IN labels(source) AND source.outgoingDependencies > 0 - ORDER BY dependency.weight DESC, source.name ASC + ORDER BY dependency[$projection_weight_property] DESC, source.name ASC LIMIT 40 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes WITH *, CASE WHEN source.clusteringHDBSCANNormalizedDistanceToMedoid >= clusterMedoidDistanceThreshold @@ -56,12 +56,12 @@ ,collect(directInNode + "\"" + sourceId + "\" -> central [" + edgeAttributes + "];") AS directInEdges WITH *, graphVizOutput + directInEdges AS graphVizOutput // Step 4: Query direct outgoing dependencies from the central node - MATCH (source)<-[dependency:DEPENDS_ON]-(central) +OPTIONAL MATCH (source)<-[dependency:DEPENDS_ON]-(central) WHERE $projection_node_label IN labels(source) AND source.incomingDependencies > 0 - ORDER BY dependency.weight DESC, source.name ASC + ORDER BY dependency[$projection_weight_property] DESC, source.name ASC LIMIT 40 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes // Use a lighter color for the target nodes of outgoing dependencies from the central node and their edges @@ -93,13 +93,13 @@ MATCH (directDependentNode)-[dependency:DEPENDS_ON]->(anotherDirectDependentNode) WHERE anotherDirectDependentNode IN directDependentNodes AND anotherDirectDependentNode <> directDependentNode - ORDER BY dependency.weight DESC, directDependentNode.name ASC + ORDER BY dependency[$projection_weight_property] DESC, directDependentNode.name ASC WITH graphVizOutput ,directDependentNode ,dependency ,collect(anotherDirectDependentNode)[0] AS firstLinkedDependentNode LIMIT 140 - WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight + WITH *, coalesce(dependency[$projection_weight_property], 1) AS weight // Use a fixed small pen width for secondary dependencies for better visibility of the more important direct dependency WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=0.3" AS edgeAttributes // Use an even lighter color for secondary dependency edges diff --git a/domains/anomaly-detection/graphs/anomalyDetectionGraphVisualization.sh b/domains/anomaly-detection/graphs/anomalyDetectionGraphVisualization.sh index b7f62aea4..695041039 100755 --- a/domains/anomaly-detection/graphs/anomalyDetectionGraphVisualization.sh +++ b/domains/anomaly-detection/graphs/anomalyDetectionGraphVisualization.sh @@ -99,10 +99,12 @@ update_markdown_references() { echo "" } >> "${markdown_graph_visualizations_reference_file}" else - { - echo "---" - echo "" - } >> "${markdown_graph_visualizations_reference_file}" + if [ "${index}" == "1" ]; then + { + echo "---" + echo "" + } >> "${markdown_graph_visualizations_reference_file}" + fi fi if [ "${index}" == "1" ]; then @@ -130,6 +132,8 @@ update_markdown_references() { # Name of the associated programming language. Examples: "Java", "Typescript" # - projection_node_label=... # Label of the nodes that will be used for the projection. Example: "Package" +# - projection_weight_property=... +# Name of the node property that contains the dependency weight. Example: "weight" create_graph_visualization() { local nodeLabel nodeLabel=$( extractQueryParameter "projection_node_label" "${@}" ) @@ -185,6 +189,8 @@ create_graph_visualization() { # Name of the associated programming language. Examples: "Java", "Typescript" # - projection_node_label=... # Label of the nodes that will be used for the projection. Example: "Package" +# - projection_weight_property=... +# Name of the node property that contains the dependency weight. Example: "weight" anomaly_detection_graph_visualization() { set_required_features "${@}" @@ -210,14 +216,15 @@ rm -rfv "${FULL_REPORT_DIRECTORY}"/*_*/${GRAPH_VISUALIZATIONS_DIRECTORY_NAME}/${ # Query Parameter key pairs for projection and algorithm side QUERY_NODE="projection_node_label" +QUERY_WEIGHT="projection_weight_property" QUERY_LANGUAGE="projection_language" # -- Detail Reports for each code type ------------------------------- -anomaly_detection_graph_visualization "${QUERY_NODE}=Artifact" "${QUERY_LANGUAGE}=Java" -anomaly_detection_graph_visualization "${QUERY_NODE}=Package" "${QUERY_LANGUAGE}=Java" -anomaly_detection_graph_visualization "${QUERY_NODE}=Type" "${QUERY_LANGUAGE}=Java" -anomaly_detection_graph_visualization "${QUERY_NODE}=Module" "${QUERY_LANGUAGE}=Typescript" +anomaly_detection_graph_visualization "${QUERY_NODE}=Artifact" "${QUERY_LANGUAGE}=Java" "${QUERY_WEIGHT}=weight" +anomaly_detection_graph_visualization "${QUERY_NODE}=Package" "${QUERY_LANGUAGE}=Java" "${QUERY_WEIGHT}=weight25PercentInterfaces" +anomaly_detection_graph_visualization "${QUERY_NODE}=Type" "${QUERY_LANGUAGE}=Java" "${QUERY_WEIGHT}=weight" +anomaly_detection_graph_visualization "${QUERY_NODE}=Module" "${QUERY_LANGUAGE}=Typescript" "${QUERY_WEIGHT}=lowCouplingElement25PercentWeight" # ---------------------------------------------------------------