Search code examples
neo4jcypher

UNWIND an empty list negatively affects outer RETURN statement


I am trying to write a Cypher statement, which manages the creation and relationships of tag nodes according to a given list of tag names for an item.

Example calling parameters:

{
  itemId: "foo",
  tagNames: ["Tag1", "Tag2", "Tag3"]
}

This is the Cypher statement:

MATCH (i:Item {id: $itemId})

OPTIONAL MATCH (i)-[previousRelations:hasTag]->(:Tag)
DELETE previousRelations
    
WITH *
CALL {
    UNWIND $tagNames as tagName

    MERGE (tag:Tag {name: tagName})
    ON CREATE SET tag.id = randomUUID(), tag.createdAt = datetime()

    RETURN tag
}

MERGE (i)-[:hasTag]->(tag)

RETURN i

The tag node creation and assignment works all fine!

My problem lies in the RETURN i statement if my $tagNames list is empty ([]). In this case the statement returns NULL instead of i but I need it to always return i.

I also tried the "FOREACH-Trick" as suggested here: https://stackoverflow.com/a/27578798/5106474

    FOREACH(ignoreMe IN CASE WHEN tag IS NOT NULL THEN [1] ELSE [] END |
        MERGE (i)-[:hasTag]->(tag)
    )

But still, same behaviour.

Thanks in advance for any suggestions!


Solution

  • That RETURN tag is reducing the cardinality to zero when $tagNames is empty. To solve this, wrap the CALL in another CALL to preserve the cardinality of the first MATCH:

    MATCH (i:Item {id: $itemId})
    
    OPTIONAL MATCH (i)-[previousRelations:hasTag]->(:Tag)
    DELETE previousRelations
        
    WITH *
    CALL {
      WITH i
      CALL {
        UNWIND $tagNames as tagName
    
        MERGE (tag:Tag {name: tagName})
        ON CREATE SET tag.id = randomUUID(), tag.createdAt = datetime()
    
        RETURN tag
      }
      MERGE (i)-[:hasTag]->(tag)
    }
    
    RETURN i