neo4jcypherneo4j-apoc

Adding a place holder node when a null value is found in vrelationship function in neo4j


I have the following query that is working as expected in neo4j. The query matches and collects distinct FEATURE, THREAT, GAP, FACT, and SCENARIO nodes with their relationships based on a specific feature key, then uses APOC procedures to create virtual relationships between these nodes for visualization, ensuring each node pair has its correct relationship type.

MATCH (fe:FEATURE)-->+(t:THREAT)-->+(g:GAP) 
MATCH (g)<--+(fa:FACT) 
MATCH (s:SCENARIO)<--+(g)
where fe.key= "Test S3 Storage#347585d0-e092-4808-a492-7b7a38e04dcd"

// Collect existing relationships and nodes
WITH collect(DISTINCT [fe, t]) + collect(DISTINCT [t, g]) AS hases,
     collect(DISTINCT [fa, g]) AS requireses,
     collect(DISTINCT [s, g]) AS scenarioGaps

// Create virtual relationships
WITH COLLECT { 
        WITH hases 
        UNWIND hases AS pair 
        RETURN [pair[0], apoc.create.vRelationship(pair[0], 'HAS', {}, pair[1]), pair[1]] AS tuple 
     } 
     + 
     COLLECT { 
        WITH requireses 
        UNWIND requireses AS pair 
        RETURN [pair[0], apoc.create.vRelationship(pair[0], 'REQUIRE', {}, pair[1]), pair[1]] AS tuple 
     } 
     + 
     COLLECT { 
        WITH scenarioGaps 
        UNWIND scenarioGaps AS pair 
        RETURN [pair[0], apoc.create.vRelationship(pair[1], 'HAS_SCENARIO', {}, pair[0]), pair[1]] AS tuple 
     }
     AS tuples

UNWIND tuples AS tuple

RETURN tuple[0], tuple[1], tuple[2]

Here is the issue I am facing:

I am unable to understand which approach should I use.


Solution

  • Try using OPTIONAL MATCH instead of MATCH for the (s:SCENARIO)<--+(g) pattern:

    OPTIONAL MATCH (s:SCENARIO)<--+(g)
    

    That allows the query to proceed when g has no path to a SCENARIO node. In that situation, the OPTIONAL MATCH would produce a NULL s value.

    If you do that, then you would have to also change your last COLLECT subquery to ignore pairs that have a NULL first value.

    For example:

    MATCH (fe:FEATURE)-->+(t:THREAT)-->+(g:GAP) 
    WHERE fe.key= "Test S3 Storage#347585d0-e092-4808-a492-7b7a38e04dcd"
    
    MATCH (g)<--+(fa:FACT) 
    OPTIONAL MATCH (s:SCENARIO)<--+(g)
    
    // Collect existing relationships and nodes
    WITH collect(DISTINCT [fe, t]) + collect(DISTINCT [t, g]) AS hases,
         collect(DISTINCT [fa, g]) AS requireses,
         collect(DISTINCT [s, g]) AS scenarioGaps
    
    // Create virtual relationships
    WITH COLLECT { 
            WITH hases 
            UNWIND hases AS pair 
            RETURN [pair[0], apoc.create.vRelationship(pair[0], 'HAS', {}, pair[1]), pair[1]] AS tuple 
         } 
         + 
         COLLECT { 
            WITH requireses 
            UNWIND requireses AS pair 
            RETURN [pair[0], apoc.create.vRelationship(pair[0], 'REQUIRE', {}, pair[1]), pair[1]] AS tuple 
         } 
         + 
         COLLECT { 
            WITH scenarioGaps 
            UNWIND scenarioGaps AS pair
            WITH pair
            WHERE pair[0] IS NOT NULL
            RETURN [pair[0], apoc.create.vRelationship(pair[1], 'HAS_SCENARIO', {}, pair[0]), pair[1]] AS tuple 
         }
         AS tuples
    
    UNWIND tuples AS tuple
    
    RETURN tuple[0], tuple[1], tuple[2]
    

    Note that I also moved the top WHERE clause immediately under the first MATCH, to filter for the desired fe.key as early as possible.